From d95f589ee7868640fcb384e2f915b3c5eb1c6b43 Mon Sep 17 00:00:00 2001 From: Raksha Date: Tue, 8 Sep 2026 17:01:54 +0000 Subject: [PATCH 1/4] feat(launch): sticky Stock chip + issuer disclaimer + fail-closed CTA MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dig #2 (Grok Super B-20 stock UX): sticky « Quote = SYMBOL (registry) » chip beside the Stock picker, Reg S / issuer disclaimer adjacent to the pick, and CTA fail-closed with an explicit chip when Stock is selected with no registry pick. Registry-gated via existing /api/quotes only. Deferred: dig #3 above-fold Stock for Instant (Instant Advanced path ships separately on feat/ol-stock-ux). --- app/src/components/launchpad/LaunchForm.tsx | 58 +++++++++++++++++---- 1 file changed, 48 insertions(+), 10 deletions(-) diff --git a/app/src/components/launchpad/LaunchForm.tsx b/app/src/components/launchpad/LaunchForm.tsx index b0617c3f..5d1ece4e 100644 --- a/app/src/components/launchpad/LaunchForm.tsx +++ b/app/src/components/launchpad/LaunchForm.tsx @@ -54,6 +54,12 @@ function randomSalt(): Hex { return `0x${Array.from(b, (x) => x.toString(16).padStart(2, "0")).join("")}`; } +function stockIssuerDisclaimer(chain: ChainKey): string { + return chain === "base" + ? "Coinbase tokenized stocks are securities issued by Coinbase under Regulation S and are not offered to persons in the US, UK, Canada, Australia, Singapore or Switzerland. That is Coinbase's rule for the stock token, not ours. The pool itself is ordinary Uniswap v4." + : "Robinhood Stock Tokens are tokenised securities issued by Robinhood and are not offered to US persons. That is Robinhood's rule for the stock token, not ours. The pool itself is ordinary Uniswap v4."; +} + type FirstBuyCtx = { pub: PublicClient; wallet: WalletClient; address: Address; V4: ReturnType["v4"]; quote: Quote; feePips: number; CHAIN: (typeof CHAINS)[ChainKey]; setPhase: (p: Phase) => void }; function parseBuyAmount(v: string, decimals: number): bigint | null | undefined { @@ -194,7 +200,7 @@ export default function LaunchForm({ ethUsd, gitlawbUsd = null, initialChain = " if (name.trim().length === 0 || name.trim().length > 32) errors.push("Name: 1–32 characters."); if (!/^[A-Z0-9]{1,10}$/.test(symbolClean)) errors.push("Symbol: 1–10 letters or digits."); if (startTick === null) errors.push("Starting market cap must be a positive number."); - if (quoteKey === "stock" && !stock) errors.push("Pick a stock to price the token in."); + if (quoteKey === "stock" && !stock) errors.push("Pick a registry stock or switch quote."); if (quoteKey === "gitlawb" && quote.usd === null && !customMcap.trim() && startTick === null) errors.push("GITLAWB price unavailable right now: enter a custom starting market cap in GITLAWB, or reload."); if (image && !/^https:\/\//.test(image.trim())) errors.push("Image must be an https URL."); if (website && !/^https:\/\//.test(website.trim())) errors.push("Website must be an https URL."); @@ -417,7 +423,7 @@ export default function LaunchForm({ ethUsd, gitlawbUsd = null, initialChain = " ) : null} {quoteKey === "stock" ? ( -
+
{stock ? ( @@ -425,15 +431,42 @@ export default function LaunchForm({ ethUsd, gitlawbUsd = null, initialChain = " // eslint-disable-next-line @next/next/no-img-element ) : null} - {stock.symbol} + Quote = {stock.symbol} + (registry) {stock.name} {stock.usd ? {fmtUsd(stock.usd)} : null} - ) : ( - setStockQ(e.target.value)} placeholder="Search ticker, e.g. AAPL" aria-label="search stock tokens" autoComplete="off" /> + <> + + Pick a registry stock or switch quote + + setStockQ(e.target.value)} placeholder="Search ticker, e.g. AAPL" aria-label="search stock tokens" autoComplete="off" /> + + )}
{!stock ? ( @@ -462,11 +495,7 @@ export default function LaunchForm({ ethUsd, gitlawbUsd = null, initialChain = " {stockHits.length === 0 ?
  • {chain === "base" ? "No match. 13 Coinbase tokenized stocks are available on Base: NVDAc, AAPLc, TSLAc, METAc, GOOGLc, AMZNc, MSFTc, MSTRc, COINc, CRCLc, INTCc, SNDKc, SPCXc." : "No match. 194 Robinhood Stock Tokens are available, e.g. AAPL, TSLA, NVDA, SPY."}
  • : null} ) : null} -

    - {chain === "base" - ? "Coinbase tokenized stocks are securities issued by Coinbase under Regulation S and are not offered to persons in the US, UK, Canada, Australia, Singapore or Switzerland. That is Coinbase's rule for the stock token, not ours. The pool itself is ordinary Uniswap v4." - : "Robinhood Stock Tokens are tokenised securities issued by Robinhood and are not offered to US persons. That is Robinhood's rule for the stock token, not ours. The pool itself is ordinary Uniswap v4."} -

    +

    {stockIssuerDisclaimer(chain)}

    ) : null} @@ -674,6 +703,15 @@ export default function LaunchForm({ ethUsd, gitlawbUsd = null, initialChain = "
    + {quoteKey === "stock" && !stock ? ( +
    + Pick a registry stock or switch quote +
    + ) : null} {errors.length > 0 && (name || symbol) ? (
      {errors.map((e) => ( From 70c0af7ac909820d94128241934c8f222edc2138 Mon Sep 17 00:00:00 2001 From: Kevin Codex Date: Thu, 10 Sep 2026 11:23:02 +0800 Subject: [PATCH 2/4] stock chip (PR #17 review): one message, issuer on the chip, no test hooks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - "Pick a stock to price the token in, or switch the quote." is one constant, shown by the validation list and the status box above the submit button; the third copy (a warm chip beside the search field) is gone. - The picked-stock chip reads "AAPLc · Coinbase stock · Apple" instead of "Quote = AAPLc (registry)": the issuer is what a buyer needs to know. - aria-live came off the whole stock section, where it re-announced the thirteen search results on every keystroke; the status box (role=status) is the only live region. - data-testid hooks removed: the repo tests source contracts, and the new launch-form test pins the single message, the issuer label, "Switch quote" resetting to the first configured quote, and the shared disclaimer helper. --- app/src/components/launchpad/LaunchForm.tsx | 22 ++++++++----------- .../components/launchpad/launch-form.test.ts | 18 +++++++++++++++ 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/app/src/components/launchpad/LaunchForm.tsx b/app/src/components/launchpad/LaunchForm.tsx index 5d1ece4e..316e60fc 100644 --- a/app/src/components/launchpad/LaunchForm.tsx +++ b/app/src/components/launchpad/LaunchForm.tsx @@ -54,6 +54,9 @@ function randomSalt(): Hex { return `0x${Array.from(b, (x) => x.toString(16).padStart(2, "0")).join("")}`; } +/** Shown wherever the form is blocked on a stock choice: one sentence, one place. */ +const STOCK_PICK_MESSAGE = "Pick a stock to price the token in, or switch the quote."; + function stockIssuerDisclaimer(chain: ChainKey): string { return chain === "base" ? "Coinbase tokenized stocks are securities issued by Coinbase under Regulation S and are not offered to persons in the US, UK, Canada, Australia, Singapore or Switzerland. That is Coinbase's rule for the stock token, not ours. The pool itself is ordinary Uniswap v4." @@ -200,7 +203,7 @@ export default function LaunchForm({ ethUsd, gitlawbUsd = null, initialChain = " if (name.trim().length === 0 || name.trim().length > 32) errors.push("Name: 1–32 characters."); if (!/^[A-Z0-9]{1,10}$/.test(symbolClean)) errors.push("Symbol: 1–10 letters or digits."); if (startTick === null) errors.push("Starting market cap must be a positive number."); - if (quoteKey === "stock" && !stock) errors.push("Pick a registry stock or switch quote."); + if (quoteKey === "stock" && !stock) errors.push(STOCK_PICK_MESSAGE); if (quoteKey === "gitlawb" && quote.usd === null && !customMcap.trim() && startTick === null) errors.push("GITLAWB price unavailable right now: enter a custom starting market cap in GITLAWB, or reload."); if (image && !/^https:\/\//.test(image.trim())) errors.push("Image must be an https URL."); if (website && !/^https:\/\//.test(website.trim())) errors.push("Website must be an https URL."); @@ -423,7 +426,7 @@ export default function LaunchForm({ ethUsd, gitlawbUsd = null, initialChain = "
    ) : null} {quoteKey === "stock" ? ( -
    +
    {stock ? ( @@ -431,8 +434,8 @@ export default function LaunchForm({ ethUsd, gitlawbUsd = null, initialChain = " // eslint-disable-next-line @next/next/no-img-element ) : null} - Quote = {stock.symbol} - (registry) + {stock.symbol} + {chain === "base" ? "Coinbase stock" : "Robinhood stock"} {stock.name} {stock.usd ? {fmtUsd(stock.usd)} : null}