feat(extension): thread chainId through threat API lookups (S-16)#6
Merged
feat(extension): thread chainId through threat API lookups (S-16)#6
Conversation
testudo-api PR #5 moves client-facing threat routes under `/api/v1/chains/:chainId/...`. Without this change, deploying the API produces 404s on every threat lookup. Ship order: extension lands in Chrome Web Store -> then API deploys. ## api-client.ts - `ApiClientOptions.chainId?: number`; `checkAddressThreat()` and `pingApi()` build URLs as `/api/v1/chains/${chainId}/threats/address/...` - `resolveChainId()` falls back to `1` (mainnet) for missing/non-finite /non-positive input — matches api-server's current behavior for missing chainId and keeps cold-start protection alive - `ThreatResponse` adds optional `matchedChainId` + `crossChainMatch` (ignored in UI for now, reserved for future cross-chain surfacing) - `checkDomainThreat()` unchanged — domain route stays global ## analysis.ts - `AnalysisDeps.checkAddressThreat` accepts `{chainId?}` - `analyzeWithCache()` / `addressCheckWithCache()` accept optional `chainId` - Threaded through `performThreeLayerAnalysis` + `performAddressOnlyCheck` - Cache key is now `${chainId}:${address}` — same address on different chains caches independently. `pendingFullAnalysis` / `pendingAddressCheck` use the same key for coalescing ## injected.tsx - Lazy `getCurrentChainId()` via `eth_chainId` RPC, cached in-module, invalidated on wallet's `chainChanged` event - `parseChainIdHex()` accepts number or 0x-hex; rejects 0/NaN/invalid - Permit + typed-data flows prefer the typed domain's chainId, fall back to active chain - EIP-7702 authorization uses the auth's chainId; when that is `0` (replay risk) the threat lookup falls back to the active chain so we still key into a real chain's registry instead of a sentinel ## background.ts + content.ts + services/messaging.ts - chainId is forwarded end-to-end through the channel and runtime message bridge. Handlers treat non-number values as `undefined` so the api-client default applies at the URL boundary ## Tests (9 new, 375 total) - api-client: URL has chains segment, custom chainId routed, default to 1 on missing / negative / NaN, pingApi routing, pingApi default - analysis: chainId forwarded to checkAddressThreat for both flows, per-chain cache partitioning, default cache key, cross-chain cache isolation - Existing tests updated to assert the new cache-key shape Build + lint clean. No behavior change when chainId is omitted — every path defaults to mainnet (1).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Unblocks the testudo-api deploy. API PR #5 moves client-facing threat routes under
/api/v1/chains/:chainId/...— without this change, deploying produces 404s on every threat lookup. Ship order: this lands in Chrome Web Store before the API rolls out.What changed
api-client.tsApiClientOptions.chainId?: numbercheckAddressThreat()+pingApi()build URLs as/api/v1/chains/${chainId}/threats/address/...resolveChainId()falls back to1for missing / non-finite / non-positive input (matches api-server behavior; keeps cold-start protection alive)ThreatResponseexposes optionalmatchedChainId+crossChainMatch(reserved for future cross-chain UX)checkDomainThreat()unchanged — domain route stays globalanalysis.tsAnalysisDeps.checkAddressThreataccepts{chainId?}analyzeWithCache()/addressCheckWithCache()accept optionalchainId${chainId}:${address}— same address on different chains caches independently.pendingFullAnalysis/pendingAddressCheckuse the same key for request coalescinginjected.tsxgetCurrentChainId()viaeth_chainIdRPC; cached, invalidated on wallet'schainChangedeventparseChainIdHex()accepts number or0x-hex; rejects0/NaN/ invalid0(replay risk) the threat lookup falls back to the active chain so we key into a real chain's registry instead of a sentinelMessage bridge
content.ts,services/messaging.ts,background.tsforwardchainIdend-to-end. Handlers coerce non-number inputs toundefinedso the api-client default applies at the URL boundaryTests — 375 pass (9 new)
api-client.test.ts
NaNpingApirouting + defaultanalysis.test.ts
checkAddressThreat(address-only + full pipeline)Test plan
yarn workspace @testudo/extension run test— 375 passyarn workspace @testudo/extension run build— clean (injected.js83.2 KB)yarn biome check packages/extension/src packages/extension/tests— cleanShip sequence
🤖 Generated with Claude Code