The note was served publicly from /brand and referenced by nothing. The
provenance it recorded already lives in a Source comment inside every SVG,
which the UI tests assert.
Viem accepts JSON-RPC error envelopes even on HTTP failures, so a single upstream error object can still crash a batched gas preflight. Return non-RPC HTTP errors for retryable failures and reject mismatched response IDs or malformed envelopes. Preserve Kevin's Arc proxy, method denials, recovery flow, and actionable error copy.
Cover real viem batching and retry behavior, keep bridge alerts within the panel, and leave unrelated local changes out. Verified 627 unit tests, 10 read-only live routes, Base/Arc approval preflights, lint, typecheck, and production build.
otherBridgeChain had no callers. docs/bridge.md now records the
maintainer's Base USDC to Arc transfer on 2026-09-16 and what the wallet
test surfaced: Phantom cannot add Arc or Robinhood, the proxy's whole-batch
403 on eth_fillTransaction, public-node rate limits, and why publicnode is
unsuitable as a proxy upstream.
viem probes eth_fillTransaction when estimating Base fees. The proxy replied
HTTP 403 for the whole batch, so every read in it failed with "unknown RPC
error" and viem never took its fallback path. Disallowed methods now get a
JSON-RPC -32601 error in their own slot while the rest of the batch is
forwarded; the allowlist itself is unchanged.
Public Base and Arc nodes rate-limit a single quote's burst of reads. They
answer inside a 200 body, sometimes with one object where a batch array was
due, which viem cannot use ("unknown RPC error" or a TypeError in its
batch scheduler). The proxy now maps rate-limit and malformed replies to
HTTP 429/502 so viem retries with backoff, and Arc reads go through the
proxy (ARC_RPC_URL upstream, else the official node) instead of straight
from every browser. The Arc origin leaves the CSP; a dev override
NEXT_PUBLIC_RPC_URL_ARC is allowed like the other chains.
viem wraps unrecognised wallet/RPC error codes as "An unknown RPC error
occurred." and keeps the provider text in details. bridgeErrorMessage now
appends that text, maps the wallet's "unrecognized chain" reply to an
actionable hint, and treats any insufficient-funds cause uniformly.
The observation that shows the discard control only decides visibility.
Removal now fetches a fresh provider status and, when a hash exists, the
source-chain receipt under the per-wallet lock, and keeps the record if
either shows activity. The button is disabled while that check runs.
Neither file is read by code or tests. They are output templates from the
contributor's design tooling and duplicate theme tokens that already live in
the stylesheet. docs/bridge.md remains the bridge documentation.
Review fixes on the Relay bridge:
- A sped-up or cancelled-and-replaced deposit no longer blocks tracking
forever. When the wallet's hash is unmined and Relay reports a different
source hash, that hash is verified as this wallet's exact deposit for the
same order and adopted.
- A record with no deposit anywhere can be discarded after 15 minutes, only
while Relay still reports "waiting" with no hashes and the source chain
has no receipt, observed within the last minute. Unmined approvals get the
same bounded discard; a late-mined approval only grants the exact
allowance every deposit re-reads.
- A wallet chain change during a quote no longer leaves the form locked on
"Finding your route…".
- Quote validity travels as ttlMs and is anchored on the browser clock at
request time, so device clock skew cannot expire or extend a quote.
- Fetch timeouts use a hand-linked signal instead of AbortSignal.any, which
older in-app wallet browsers lack.
- Relay's chain catalogue is cached per fetcher for a minute instead of
re-downloaded on every quote; an unverifiable copy is dropped.
- The USDC approval is no longer gated on the 45 s quote TTL, since a fresh
quote is required after it anyway.
- Wallet and RPC errors go through friendlyError; the dialog states that
Openlaunch does not operate Relay or hold funds in transit.
Unit tests cover the hash adoption rule, the quoting-lock reset, expiry
anchoring, the linked timeout signal, both discard gates and the catalogue
cache. 621 tests, lint, typecheck, build and the read-only live route suite
pass.
Replace letter placeholders with the existing official Base and Robinhood assets. Switch the Robinhood mark through theme CSS to preserve contrast without adding hydration state or changing wallet actions.
CodeQL mistook the split-array includes assertion for substring URL validation. Use Set membership for every expected origin so the test's token boundary is explicit without changing production CSP behavior.
Let users fund the same wallet across Base, Robinhood and Arc without leaving Openlaunch or deploying new contracts. Validate Relay orders and deposits, require exact USDC approvals, and preserve uncertain transfers for recovery instead of resending.
Include route, approval, recovery and UI regression tests, official asset provenance, and rollout documentation. Keep Robinhood USDC disabled because current routes fail the deposit and impact safeguards.
An empty feed records nothing, so treating it as the post-gap baseline let the
next populated poll surface the missed activity. The gap stays open until a
populated snapshot is seen. (CodeRabbit)
The missing-contracts explanation helps a developer, but a visitor seeing a
chain that is not live yet (Arc, next) should read it as upcoming. Production
builds keep "Coming soon."; development still says the contract settings are
missing.
Cards show one at a time for 6s, so 20 queued let "just launched" and buy
cards run up to two minutes behind. Ten keeps what is on screen within about a
minute; the newest activity is kept and own confirmations are not capped.
A timeout can fire a moment before Date.now() reaches the deadline. expireToast
then returned the same state, the effect never re-ran, and the card plus
everything queued behind it (own confirmations included) stayed on screen
until dismissed. The callback now waits out the remainder. (CodeRabbit)
Polling pauses while the tab is hidden, so the first snapshot back marked
everything missed as new and queued up to 20 old activity cards, shown one at
a time for minutes. Snapshots more than 30s apart by server time now refresh
what has been seen without queueing it; live activity resumes on the next
poll. Server times are compared with each other, so client clock skew does
not matter.
Live launches, buys and sells are the social proof of engagement, so a
visitor sees them unless they turn them off in notification settings. Only
an explicit saved off mutes them; a missing, unknown or cleared value falls
back to on, and the server snapshot matches that default.
Typed and pasted text is uppercased in the field itself before React sees the
change, so the caret stays put when editing mid-word and fast keystrokes land
in order. An in-progress IME composition is left as typed and uppercased once
it is committed, so candidate windows keep working.
Every collect pays beneficiaries both pool currencies (quote from buys, the
launched token from sells), but the fee panel and dashboard only read the
quote side. The token page now shows collected, burned and paid amounts for
both, with a Claim button per credited currency. The dashboard counts
token-side uncollected fees (a sells-only pool was not collectable) and
prices the token share into earned at the pool price, marked with ≈.
The locker has always accepted up to 7 recipients with bps shares, but the form
could only name one beneficiary or burn. "Someone else" is now a split editor:
address + percent rows, quick adds for the connected wallet and a burn share
(0x…dEaD), and a running total.
- lib/launchpad/recipients.ts: pure parsing and validation. Percent is parsed as
integer bps (two decimals = one bp, finer is rejected, never rounded); shares
must total exactly 100%; no zero address, no duplicates, 1 to MAX_RECIPIENTS rows.
- MAX_RECIPIENTS in config, pinned to LaunchLocker.sol by a test.
- Summary chip, fee tile and routing line derive from the recipients actually
sent; an unfinished split previews as a split, never as a burn.
- Share card label says "beneficiaries" for more than one.
Keep IME input stable and make community activity opt-in so market events do not crowd out personal confirmations. Clarify permanent fee routing without changing launch economics or transaction payloads.
JS template cooked text consumes a single backslash, so ESCAPE '\' in source was cooking to ESCAPE '' (empty escape) and the helper's backslashes were ignored. Using ESCAPE '\\' in source cooks to ESCAPE '\' in SQL, matching escapeLike('\%','\_','\\').
Verified: search.test.ts 9/9; query-construction check shows ESCAPE '\' in cooked SQL.
- Make fn().catch cleanup conditional: only delete when cached value is still the failing promise, so an evicted in-flight request that later rejects does not delete a successful replacement under the same key (reproduces at constant clock: pending -> 200 inserts evict -> reload -> old reject).
- Fix expiry check to >= so entries at exactly TTL are considered expired, matching the hit test (<) and avoiding evicting fresh entries while leaving expired ones.
fmtUnitsExact formats the raw 18-dec string with BigInt arithmetic (thousands separators, fraction trimmed
of trailing zeros), so the title never rounds through a float. The compact figure in the row is unchanged.
The network breakdown gains one line: GITLAWB sent to 0x…dEaD by GITLAWB-quoted launches, one combined
figure for both chains (one supply bridged over LayerZero), shown as an amount, never in USD. It sums
fees_quote_burned over GITLAWB-quoted launches; GITLAWB is only ever the quote side, so that is the whole burn.
Along the way:
- The live poller merges totals field by field, so a poll that lands on an old machine during a rolling
deploy keeps the last value of a newly added total instead of dropping it.
- The receipt sync path no longer writes a Burned/Paid event for a position whose launch is not indexed
yet: such a row was pinned with token NULL and never rolled into any total (the poller's later pass hit
ON CONFLICT and the rebuild script joins on token). It now leaves the event for the poller, which
indexes the launch first. The rebuild script attaches any historical orphaned rows by position id.
- The dollar totals (volume, fees to recipients, fees burned) fold every quote through its USD price and
silently counted a quote at $0 while its price was unknown (cold start, a feed down >15 min). The totals
now carry usd_partial and the hero shows ≈ with a tooltip while that is the case.
Limit build-only CI tokens to read-only contents permissions.
Clarify intentional control filtering without changing signed post text,
and replace the sitemap test hostname-prefix check with exact origins.
Cover the image-preview React attribute boundary without changing its
runtime behavior or dismissing the existing scanner warning.
The indexer stores a swap with a null trader when the sender lookup fails
(a rate-limited node during a busy launch block). The holder panel then
called toLowerCase on it, the token page's error boundary replaced the
whole page, and /api/launch/holders returned 500 for that token.
- sniperSummary / creatorActivity skip swaps without a trader
- the sender lookup retries once before giving up
- healSwapTraders re-reads a bounded batch of null-trader swaps every poll
- sync had no limit despite being the most expensive route (tx-less calls fan
out over every chain); 10/min per IP
- meta is unsigned by design, so the IP bucket is the only spam defense;
20/min per IP before validation
- presence hashes caller-controlled UA into a DB row per variant; 30/min per
IP after the bot short-circuit
- write-limits.test.ts pins the wiring (429 shape, limit-before-work order)
- the old sweep only deleted expired entries, so a burst of fresh
request-controlled keys (candle buckets, per-limit list keys) grew the map
without limit inside one TTL window
- expiry now uses each entry's own TTL; overflow evicts expired first, then
oldest; fresh hits refresh recency so hot keys survive cold bursts
- new memo.test.ts: hits, rejection retry, in-flight sharing, cap, LRU, TTL
- searchLaunches pre-limit and JS tiebreak used raw block numbers across
chains, hiding exact matches on low-height chains; both now use block
time (chain id, block number tiebreaks), matching the newestFirst invariant
- findLaunchChain picks the newest row by block time, not height
- escape %/_ in ILIKE patterns (ESCAPE '\') so they match literally
- compareSearchHit + escapeLike are pure and unit-tested
- declare tradeSlippageBps outside try, assign after preflight awaits
- catch uses it when set, else default (preflight errors are never slippage reverts)
- parseSlippageField rejects minus/letters, normalizes decimal comma to dot
- input handler no longer strips chars before parsing (0,5 no longer becomes 5%)
- trade captures slippageBps per transaction and passes it to friendlyError