Commit Graph
200 Commits
Author SHA1 Message Date
Kevin Codex 7b717965cd indexer: the receipts fallback validates the RPC shape before reading it
A null receipt, a non-array logs field or a malformed log would have thrown
outside the guarded request and wedged the range; anything not shaped like
receipts now counts as "unavailable" and the skip-with-alert path runs.
2026-09-16 23:00:58 +08:00
Kevin Codex 86ba4b80ae arc: CodeRabbit follow-ups from PR 51
- Dockerfile: declare the Arc build args in the runtime stage too (ARG does
  not cross FROM; the image's own env for them was empty and production only
  worked because the same values are Fly secrets)
- indexer: a single token's Transfer logs in a block over the node's result
  cap are read from the block's receipts (eth_getBlockReceipts is uncapped;
  Arc's node has it) before the block is ever skipped
- verify.sh: a failed verification fails the script after every target ran
- form copy: "same launch, same rules" (the Arc factory carries a guard)
- LAUNCHPAD.md: why the Arc factory's copied NatSpec stays as deployed
2026-09-16 22:42:05 +08:00
Kevin Codex c473b1f6e3 Merge pull request #51 from Gitlawb/feat/arc
Arc: third chain, quoted and gassed in USDC
2026-09-16 21:53:53 +08:00
Kevin Codex b4388805a4 chains: one table of RPC env names, no dynamic call
CodeQL's autofix replaced the per-chain lookup with a switch, which loses the
registry guarantee that a new chain fails the typecheck until it names its RPC
variable. RPC_ENV_NAME in chainKeys.ts keeps that guarantee, is indexed without
a dynamic method call, and is shared by the server module and the test mock.
2026-09-16 21:45:21 +08:00
Kevin Codex 8257e97bc2 contracts: Arc deployment record
LaunchFactoryArc 0x815542E8b392389A1389E22E588E4B62A67Ade72 and LaunchLocker
0xcd1680D26922fcd9CabFbb8a56bA40C333fD842a on Arc (5042), block 21165817, tx
0x9814d588…6a4b7, deployed 2026-09-16 from the same deployer at nonce 0: the
same addresses as Base and Robinhood Chain. Deployed bytecode checked against
the local build (identical, immutables aside) and the locker against Base's.
Sourcify: exact match for both; the Arc explorer's API was still gated.
2026-09-16 21:44:01 +08:00
Kevin CodexandCopilot Autofix powered by AI 66e397e4e0 Potential fix for pull request finding 'CodeQL / Unvalidated dynamic method call'
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
2026-09-16 21:40:28 +08:00
Kevin CodexandCopilot Autofix powered by AI 205fe24f78 Potential fix for pull request finding 'CodeQL / Unvalidated dynamic method call'
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
2026-09-16 21:35:18 +08:00
Kevin CodexandCopilot Autofix powered by AI 9e3e72fd72 Potential fix for pull request finding 'CodeQL / Incomplete URL substring sanitization'
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
2026-09-16 21:35:07 +08:00
Kevin Codex 34f0b0407b arc: final review round (live fees, silent skips, unindexed chains)
- Arc gas reserves sized to launch-day fees with margin: the base fee ran
  ~80 gwei plus ~30 gwei tips (measured), four times the 20 gwei floor the
  reserves were sized at, so a first buy right after a launch could have
  failed for gas. Launch reserve 1 USDC, swap reserve 0.2 USDC; blurb updated.
- the single-token holder skip logs an alert and says what stays off; the
  "holds only gas" copy is right where the quote is the gas token
- /api/health flags a configured chain the indexer has never run for; the
  secrets script requires the Arc locker, deploy block and RPC once the Arc
  factory is set, so Arc cannot go live unindexed
- the sync-lag alert threshold is per chain (block times differ ~20x) with
  the global and per-chain env overrides
- LAUNCH_SYNC_CONFIRMATIONS_ARC stays pushable as an override
2026-09-16 21:32:06 +08:00
Kevin Codex 2366a6ed0e arc: third review round
- sync errors are stored with every URL redacted: the message reaches
  /api/health unauthenticated and a keyed provider URL carries its API key
- a chain that finalizes every block keeps 0 confirmations regardless of the
  global LAUNCH_SYNC_CONFIRMATIONS, so no Arc-only variable is needed
- holder Transfer fetches bisect blocks once; the token list only splits at a
  single over-cap block, and one token still over the cap has that block skipped
- quoteUsdOf prices the ETH quote by key; a bare address(0) is no longer ETH
- contracts/deployments/launchpad-arc.json records Arc's Uniswap v4 addresses
  (factory and locker filled at deploy time) and a test pins the app's v4
  config to every chain's record
- the hero tolerates a poll from a machine that predates a chain (per-chain
  totals merge one level deeper, and a missing bucket counts as 0)
- the dev env example leaves the Arc fork override commented so Arc reads go
  through the proxy unless a third anvil is started
- the wallet menu shows the bridge hint on a registry chain the site cannot
  launch on yet; ?chain= for such a chain reads as all chains in the list,
  the live and list APIs and the home page
- LAUNCH_FACTORY_ABI names NativeQuoteUnsupported with a friendly message
- the bridge reads CHAINS.arc directly; the alias wrappers are gone
2026-09-16 21:26:04 +08:00
Kevin Codex 2815423be2 wallet menu: drop a stale comment left by the rebase 2026-09-16 21:26:04 +08:00
Kevin Codex ce3c7eec70 arc: one Arc for the launchpad and the bridge (rebase on main)
main's bridge (PR 49) registered Arc on its own for bridging. After the rebase
the bridge reuses the registry's chain: one definition, the RPC proxy has no
Arc special case, wagmi registers every chain through the same proxied
transport, and the wallet switcher shows the official Arc mark next to the
Base and Robinhood ones as a per-chain record.

Tests follow: the chain mock reads the per-chain RPC env like the real module
(the Arc proxy test sets ARC_RPC_URL), the bridge tests expect Arc as a
registry chain, and the chain-branching guard skips the bridge directories,
which speak Relay's own chain ids and names by design.
2026-09-16 21:26:04 +08:00
Kevin Codex 06e598591b arc: unconditional guard in LaunchFactoryArc; second review round
LaunchFactoryArc is only ever deployed on Arc, so its native-quote refusal no
longer switches on the chain id: the diff to LaunchFactory.sol is now the
contract note, the name, the error and one revert line. The sweep demo in the
fork suite deploys the plain LaunchFactory on the Arc fork instead of cheating
the chain id.

App: the wallet switcher and the list/feed chain filters offer configured
chains only (all chains in development), the stock USD and decimals arms in the
list query are scoped by chain like every other arm, the indexer skips a
single token's holder update for a block whose Transfer logs exceed the node's
cap instead of wedging, blank confirmation env values read as unset, the
insufficient-funds error no longer says ETH, and the last two-chain strings
are gone. verify.sh scopes address overrides to Arc; the drift check ignores
trailing whitespace.
2026-09-16 21:26:04 +08:00
Kevin Codex 515088e170 contracts: LaunchFactoryArc as a separate file; LaunchFactory.sol back to the deployed source
The Arc guard lives in src/LaunchFactoryArc.sol, a verbatim copy of
src/LaunchFactory.sol plus the native-quote refusal, so the file Base and
Robinhood Chain were deployed and verified from stays byte-identical.
script/check-arc-factory.sh (run in CI) fails if the two files drift apart in
anything but the guard; docs/LaunchFactoryArc.diff records that diff.

The deploy script deploys LaunchFactoryArc on chain 5042 and LaunchFactory
elsewhere; verify.sh submits the Arc factory under its own source; the Arc
fork suite and the guard unit test target LaunchFactoryArc.
2026-09-16 21:26:04 +08:00
Kevin Codex 0d2b9ddbdb contracts: refuse native quotes on Arc; nonce guard; rug-proof and hazard fork tests
On Arc the native asset is USDC and the same balance is also the ERC-20 at
0x3600…0000. LaunchLocker keeps one ledger per currency, so a native-quoted
position there would read a credited ERC-20 share (a payout on Circle's
blocklist; three sanctioned addresses are blocklisted on Arc's USDC today) as
native surplus and pay it to another launch's recipients, after which every
smaller USDC collect underflows. Found in the pre-deploy contract review.

LaunchFactory.launch now reverts NativeQuoteUnsupported for quote = address(0)
when block.chainid is 5042 (Arc-only; Base and Robinhood unchanged, and the
locker is byte-identical everywhere). The Arc fork suite reproduces the sweep
with the guard cheated off, pins the refusal, and ports the rug-proof
assertions to Arc's PositionManager; a unit test pins the chain scoping.

The deploy script refuses to run from a non-zero deployer nonce unless
ALLOW_NONZERO_NONCE=true, since nonce 0 is what keeps the addresses identical;
verify.sh takes FACTORY/LOCKER overrides.
2026-09-16 21:26:04 +08:00
Kevin Codex e3cb18baec arc: review fixes across the form, list, trade panel and indexer
- GITLAWB filter chip is offered only on chains that have GITLAWB (filters now
  carry a chain list; Arc has none)
- /launch?chain=<unconfigured> mounts on a configured chain instead of a
  disabled tile (production, before the Arc contracts are set)
- one shortfall, one message: the native-gas checks skip a quote that already
  had the reserve applied (USDC on Arc)
- trade panel keeps a per-chain swap reserve for native and shared-gas buys
  (an Arc pool quoted in native USDC no longer reserves an ETH-sized dust)
- indexer: a single block over the node's log cap splits the token list
  instead of wedging the cursor; a query timeout is no longer bisected
- one chain-scoped quote-arm helper feeds the GITLAWB, USDG and USDC matches
- metadata validation error, env example, secrets script (Arc RPC optional
  until Arc is live) and stale two-chain copy updated
2026-09-16 21:26:04 +08:00
Kevin Codex ed354a4fb7 pricing: address(0) is the chain's native asset, not always ETH
The factory is permissionless and documents address(0) as the native quote, so
a pool quoted in it can exist on Arc whether or not the form offers one. Every
USD conversion priced address(0) at ETH on every chain, so a $10k native-quoted
Arc launch would have shown a multi-million-dollar market cap, topped the mcap
and volume sorts, the feed, share cards and the public API, and inflated the
hero totals. Found in the pre-merge security review.

NATIVE_QUOTES names what address(0) is per chain (ETH; USDC at 18 decimals and
a fixed dollar on Arc). quoteInfo resolves it, the SQL stable arm carries it,
the ETH arm is scoped to the chains whose native asset is ETH, Arc native volume
joins the USDC bucket at 6 decimals, and the trade panel decides native by
address so such a pool trades with value rather than through Permit2.
2026-09-16 21:26:04 +08:00
Kevin Codex 95f6fde767 contracts: pin burn-mode USDC fees reaching dEaD on Arc
LaunchLocker._burn moves an ERC-20 fee to 0x…dEaD with a plain transfer, and
would make collect() revert forever if the token refused it. Circle's USDC has
a blocklist and Arc reverts transfers to some special addresses, so the Arc fork
suite now launches in burn mode, buys, collects, and asserts the whole USDC fee
landed at dEaD (checked against live Arc: dEaD is not blocklisted).
2026-09-16 21:26:04 +08:00
Kevin Codex 24b655fda7 contracts: Arc deploy target, verifier, and a fork suite under arc-foundry
DeployLaunchFactory knows Arc's PoolManager and PositionManager; verify.sh
targets the Arc Blockscout. The new fork suite deploys the factory on a fork of
Arc mainnet and proves a USDC-quoted launch, buy and fee collection on the live
Uniswap v4, that native and ERC-20 USDC are one balance, and that the live
Universal Router decodes the v2 ExactInputSingleParams layout and rejects v1.

It only runs under Circle's arc-foundry with the arc profile: USDC's ERC-20
face moves native balance through an Arc-only opcode, so upstream forge reverts
every transfer with OpcodeNotFound. Under plain forge the suite skips.
2026-09-16 21:26:04 +08:00
Kevin Codex 636f27b138 arc: third chain, quoted and gassed in USDC
Adds Arc (chain 5042, Circle's L1, public mainnet 2026-09-16) to the registry
behind the existing configured-chain gate: it reads "Coming soon" until the
factory env is set, so this ships before the contracts do.

- chain definition with USDC as the 18-decimal native asset and the public RPC
  as the fallback; ARC_RPC_URL (Alchemy) overrides it server-side, the browser
  keeps using the same-origin proxy
- Uniswap v4 addresses from Uniswap/contracts 5042.md, Universal Router v2.1.1
  with the v2 swap layout (proven on a fork, see the contracts commit)
- a "usdc" quote at a fixed dollar on the Permit2 path, with presets, the list
  filter, per-chain totals, and SQL pricing that no longer special-cases USDG
- gas reserve per chain, and the Arc quirk it exists for: the USDC quote IS the
  gas token, so a first buy or a trade for the whole balance is refused and the
  suggested buy leaves the reserve in the quote's own units
- stock source moved into client-safe config so the Stock quote button is not
  offered on a chain without a registry
- indexer: per-chain confirmations (Arc finalizes every block: 0) and log
  fetches that halve their range when a node caps results per call (Arc: 2000)
- env plumbing for the Arc factory, locker, deploy block and RPC across the
  Dockerfile, fly config, secrets script and env example; site copy names all
  three chains
2026-09-16 21:26:04 +08:00
Kevin Codex 96ff128dc5 chains: one registry instead of Base-or-Robinhood branches
Every per-chain value now comes from a Record<ChainKey, …> keyed off a pure
chain list (src/lib/chainKeys.ts), so adding a chain fails the typecheck until
it has its RPC env, explorer, deploy block, swap site, stock source, GITLAWB
address and form copy. Before, ~25 two-way ternaries sent any non-Base chain
down the Robinhood arm (RPC env, deploy block, stock registry, copy), and the
sitemap, health route and posts regex hardcoded both chain ids or names.

- chainKeys.ts: keys, ids, labels, explorers, isChainKey/chainKeyOf/chainKeyOr,
  chainList for copy; seo.ts reuses it instead of a second ChainKey
- server env lookups (RPC, deploy block) and NEXT_PUBLIC dev RPCs as records
  (literal process.env reads kept so Next still inlines them)
- stocksServer: explicit STOCK_SOURCE per chain; unknown chains get no stocks
- GITLAWB_ADDRESSES allows null; the gitlawb SQL match is built from it
- wagmi config, chain toggles, network glyphs, badges, locker proof links and
  hero per-chain counts all map over CHAIN_KEYS
- schema: drop the chain_id DEFAULT 8453 so an insert without a chain fails
  instead of being filed under Base (every insert already names chain_id)
- chainKeys.test.ts guards against new chain-name branches and hardcoded ids

No behaviour change for Base or Robinhood.
2026-09-16 21:26:04 +08:00
Kevin Codex 3d4e4ddc7f Merge pull request #48 from Gitlawb/fix/feed-dust-floor
Fix/feed dust floor
2026-09-16 21:19:58 +08:00
Kevin Codex 0233b369b3 Merge pull request #50 from Gitlawb/codex/bridge-approval-recovery
fix(bridge): recover stuck approvals safely
2026-09-16 21:19:40 +08:00
Vasanth T 92172b78cc fix(bridge): recover stuck approvals safely
Explain missing, queued and underpriced approvals without treating
missing receipts as failed transactions. Block visible wallet queues
before a new approval or deposit signature.

Verify mined replacement hashes against the exact approval and fresh
allowance. Prevent delayed polling from undoing confirmed recovery.
No nonce overrides, automatic retries or contract changes.
2026-09-16 18:34:53 +05:30
Kevin Codex c6fcf0704a Merge pull request #49 from Gitlawb/codex/bridge
feat(bridge): add Relay bridge for Base, Robinhood and Arc
2026-09-16 20:16:52 +08:00
Kevin Codex e6e5e2cadc brand: drop the assets README; each SVG carries its own source comment
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.
2026-09-16 20:11:49 +08:00
Vasanth T f5c9400473 fix(rpc): keep malformed batch errors retryable
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.
2026-09-16 17:33:01 +05:30
Kevin Codex 2eaa192c13 bridge: drop an unused export; record the wallet test and its findings
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.
2026-09-16 19:56:59 +08:00
Kevin Codex ed258b71cf rpc proxy: answer disallowed methods in place instead of failing the batch
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.
2026-09-16 19:45:05 +08:00
Kevin Codex 945d57134c bridge: proxy Arc reads and make the RPC proxy retry-friendly
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.
2026-09-16 19:37:35 +08:00
Kevin Codex cc5428e847 bridge: show the provider's own reason behind generic RPC errors
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.
2026-09-16 19:21:29 +08:00
Kevin Codex a0f59f4d93 bridge: discard re-verifies Relay status and the receipt under the lock
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.
2026-09-16 19:03:09 +08:00
Kevin Codex 1cb3c26eeb bridge: drop the design-tool artifacts (DESIGN.md, bridge-surface.md)
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.
2026-09-16 18:49:58 +08:00
Kevin Codex 7ec59657a5 bridge: unstick stranded records, anchor quote expiry, drop AbortSignal.any
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.
2026-09-16 18:35:55 +08:00
Vasanth T 81278787f3 fix(wallet): use official network logos
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.
2026-09-16 14:07:56 +05:30
Vasanth T 718c8f3556 test(csp): assert exact connect-source membership
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.
2026-09-16 13:54:49 +05:30
Vasanth T 4ae0a6ceff feat(bridge): add Relay funding across three chains
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.
2026-09-16 13:39:13 +05:30
Kevin Codex d6f13f443a feed: keyset swap pages; a swap is identified by its log (CodeRabbit, PR #48)
Two notes on the paged read, both taken:

- Identity. bb_launch_swaps is keyed (chain_id, tx_hash, log_index) and
  the indexer stores each PoolManager.Swap log on its own, yet the feed
  deduped on chain:tx:token:amount, so two equal swaps in one tx would
  have collapsed into one row. The swap feed item now carries log_index
  and the dedupe key is chain:tx_hash:log_index. The tape and the toast
  tracker key swaps the same way, so the same pair stays two rows there
  too instead of colliding as React keys.
- Consistency. Offset pages over a live table could skip a swap indexed
  between two reads. Pages are now keyset: the cursor is the last row
  seen and each page reads rows strictly older on the complete unique
  ordering (block_time, log_index, chain_id, tx_hash), with a leading
  block_time <= bound so the block_time index does the work (index scan,
  49 rows read for a 48-row page on the production copy). No
  transaction on the request path, no OFFSET.

Tests: the pager's fake source is keyset too; a new case inserts a swap
at the head between page reads and shows nothing skipped or repeated;
source contracts pin the cursor clause, the full ordering, the log-index
identity in the feed item and in the tape/toast keys.
2026-09-16 09:33:27 +08:00
Kevin Codex d5a50eb56a feed: page swaps until the dust filter fills the quota (CodeRabbit, PR #48)
A fixed 2n over-fetch let 30 dust swaps ahead of 24 real ones shrink
the feed to 18 rows while older real trades sat unread. The swap arm
now pages: collectNonDust reads 2n newest swaps, then older pages only
while dust keeps the count short, stopping when the quota is met, the
source runs dry, or FEED_SWAP_PAGES (4) pages have been read, so a bot
flood is still a bounded read (index scan on block_time + incremental
sort, no table scan). Pages are offset-based over a live table, so a
row that shifts between two reads is dropped by the tape's own key.
Launches and swaps are read separately and merged newest-first.

Regression test: 30 dust swaps ahead of 24 real ones yield all 24, with
exactly one extra page read; plus tests for the dry source, the page
cap, and a row shifting across a page boundary.
2026-09-16 09:22:45 +08:00
Kevin Codex 5a813a796b feed dust: a price that reads 0 is no price, not a verdict
isDustSwap priced a swap whenever usd was a finite number, so a quote
whose live price momentarily read 0 (a GITLAWB pool hiccup, a stock feed
returning nothing sensible) would have marked every trade in that quote
as dust and emptied it from the feed. Only a positive price decides;
otherwise the display floor applies as with no price at all.
2026-09-16 09:04:54 +08:00
Kevin Codex 481c29b3ed feed dust: cover the wiring and the toast titles
- tx-toasts.test.ts: the harness accepts binding overrides; a new test
  runs the real fmtQuote through the actual TxToasts handler and checks
  that a 4,999-raw USDG buy toasts as "Buy <0.01 USDG of DUSTY", a
  sub-5-gwei sell as "Sell <0.00000001 ETH of DUSTY", 5,000 raw as
  "Buy 0.01 USDG", and that no card ever reads as a zero-amount trade.
- feed-dust-wiring.test.ts: source contracts that getLaunchFeed
  imports dropDust, over-fetches 2n swaps while the launch arm stays at
  n, keeps every candidate row in the union, returns dropDust(items, n)
  last, and puts no raw-wei floor in SQL; that the tape, the toasts and
  the trade table all format swap amounts through fmtQuote with the
  quote's decimals; and that the trade table's symbol-less cell reads
  "<0.01" rather than "0" for a dust row.
2026-09-16 09:01:56 +08:00
Kevin Codex 8160d3a75b feed: drop dust trades and never print a real amount as "0"
A Uniswap v4 swap can move 1 wei of quote, and the tape and the toasts
were announcing such trades as "Buy 0 USDG": fmtQuoteUnits rounded
anything under half a cent (or under 5 gwei for 18-dec quotes) to a
bare "0". A zero delta can never be recorded as a buy, so every one of
those rows was a real dust trade shown dishonestly.

- feed-dust.ts: swaps worth less than FEED_DUST_USD (one cent) are
  dropped from getLaunchFeed after pricing; with no USD price the rule
  falls back to the display floor, so whatever would have read "0" is
  dust while a price feed is down. Launches always pass. The swap arm
  over-fetches 2n rows so a burst of dust cannot empty the feed. The
  token trade table, totals, candles and ranking are untouched.
- fmtQuoteUnits: a non-zero amount under the display floor prints
  "<0.01" / "<0.00000001"; only an exact zero prints "0", so empty
  volume stats are unchanged.
2026-09-16 08:29:56 +08:00
Kevin Codex c51e0ce080 Merge pull request #40 from Gitlawb/codex/input-notification-fixes
fix(app): improve launch input, fees and notifications
2026-09-15 15:32:03 +08:00
Kevin Codex bdda69282b toasts: an empty snapshot after a gap does not complete the baseline
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)
2026-09-15 15:28:31 +08:00
Kevin Codex d8744a4489 launch form: unconfigured chains read "Coming soon" in production
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.
2026-09-15 15:21:24 +08:00
Kevin Codex 37da150854 toasts: queue at most 10 activity cards so a busy stream stays under a minute old
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.
2026-09-15 15:18:30 +08:00
Kevin Codex 55c4c7cb27 notification settings: only promise persistence when storage works
When the browser refuses storage the choice lasts for the current page only,
so the footer says so instead of "Saved in this browser". (CodeRabbit)
2026-09-15 15:04:03 +08:00
Kevin Codex 5adcaf2abc toasts: re-arm a timer that fires early instead of freezing the queue
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)
2026-09-15 15:04:03 +08:00
Kevin Codex 738228c8e6 toasts: a poll after a hidden-tab or offline gap is a baseline, not a replay
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.
2026-09-15 14:55:17 +08:00
Kevin Codex 69a53656bc notifications: live activity on by default
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.
2026-09-15 14:55:17 +08:00