fix(discovery): use actual slab size in memcmp fallback, not hardcoded V12_1 large#183
Conversation
…d V12_1 large
Both memcmp fallback paths in discoverMarkets (empty-result fallback at
line 686 and error-catch fallback at line 712) hardcoded maxAccounts=4096
and dataSize=SLAB_TIERS.large.dataSize for all returned accounts. This
caused detectSlabLayout to fail for any slab that wasn't V12_1 large
(the vast majority of slabs), silently dropping them with an
"unrecognized layout" warning.
The root cause was that the memcmp queries used dataSlice to fetch only
1940 bytes, making data.length always 1940 regardless of actual slab
size. With the fake dataSize hint, detectSlabLayout looked for a layout
matching V12_1 large — which only matches actual V12_1 large slabs.
The fix removes dataSlice from both memcmp queries so the full account
data is returned. This makes data.length equal to the actual slab size,
which is then passed to detectSlabLayout for correct layout detection.
maxAccounts is derived from the detected layout instead of hardcoded.
The trade-off is higher bandwidth for the memcmp path (full slab data
vs 1940-byte slice). This is acceptable because:
1. The memcmp path is already a last-resort fallback (tier 1 dataSize
filters and tier 2 REST API both failed)
2. The accounts already passed the PERCOLAT magic bytes filter
3. Correct layout detection is worth the extra bandwidth — the
alternative is silently dropping valid markets
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
📝 WalkthroughWalkthroughThe fallback logic in Changes
Sequence DiagramsequenceDiagram
participant discoverMarkets
participant RPC as RPC / getProgramAccounts
participant detectLayout as detectSlabLayout
participant results as RawEntry Results
discoverMarkets->>RPC: Tiered queries with dataSlice
RPC-->>discoverMarkets: Zero results
discoverMarkets->>RPC: Fetch full slab accounts (no slice)
RPC-->>discoverMarkets: Full account data
loop For each account
discoverMarkets->>discoverMarkets: Compute actualSize from account.data.length
discoverMarkets->>detectLayout: detectSlabLayout(actualSize, data)
detectLayout-->>discoverMarkets: Detected layout with maxAccounts
discoverMarkets->>results: Build RawEntry with layout.maxAccounts & actualSize
end
results-->>discoverMarkets: Fallback discovery results
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/solana/discovery.ts (1)
728-736: LGTM — consistent fix applied to the error-catch fallback path.The same pattern is correctly applied here, ensuring both fallback paths behave identically.
Minor note: the mapping logic on lines 702-710 and 728-736 is now duplicated. Consider extracting to a small helper function for DRY, though this is optional given the limited scope.
,
♻️ Optional: Extract shared mapping logic
// Helper near the top of discoverMarkets or as a module-level function function toRawEntries( accounts: Awaited<ReturnType<Connection["getProgramAccounts"]>>, ): RawEntry[] { return accounts.map(e => { const actualSize = e.account.data.length; const layout = detectSlabLayout(actualSize, new Uint8Array(e.account.data)); return { ...e, maxAccounts: layout?.maxAccounts ?? 4096, dataSize: actualSize, }; }); }Then both fallback paths become:
rawAccounts = toRawEntries(fallback);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/solana/discovery.ts` around lines 728 - 736, The mapping that converts getProgramAccounts results into RawEntry objects is duplicated (lines mapping to rawAccounts in discoverMarkets); extract that logic into a helper (e.g., toRawEntries) that accepts the accounts array, computes actualSize, calls detectSlabLayout(new Uint8Array(...)), sets maxAccounts = layout?.maxAccounts ?? 4096 and dataSize = actualSize, and returns RawEntry[]; then replace both inline map usages with rawAccounts = toRawEntries(fallback) (and the other occurrence) to remove duplication while preserving behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@src/solana/discovery.ts`:
- Around line 728-736: The mapping that converts getProgramAccounts results into
RawEntry objects is duplicated (lines mapping to rawAccounts in
discoverMarkets); extract that logic into a helper (e.g., toRawEntries) that
accepts the accounts array, computes actualSize, calls detectSlabLayout(new
Uint8Array(...)), sets maxAccounts = layout?.maxAccounts ?? 4096 and dataSize =
actualSize, and returns RawEntry[]; then replace both inline map usages with
rawAccounts = toRawEntries(fallback) (and the other occurrence) to remove
duplication while preserving behavior.
Summary
Both memcmp fallback paths in
discoverMarketshardcodedmaxAccounts: 4096anddataSize: SLAB_TIERS.large.dataSizefor all returned accounts. This causeddetectSlabLayoutto fail for any slab that wasn't V12_1 large, silently dropping valid markets with an "unrecognized layout" warning.The root cause: memcmp queries used
dataSlice(1940 bytes), makingdata.lengthalways 1940. The fakedataSizehint then fed a V12_1-large size todetectSlabLayout, which only matches actual V12_1-large slabs.Changes
dataSlicefrom the empty-result memcmp fallback; detect layout from actualdata.lengthBoth paths now use
detectSlabLayout(data.length, data)to derivemaxAccountsanddataSizefrom the actual slab data.Trade-off
Higher bandwidth for the memcmp path (full slab data vs 1940-byte slice). Acceptable because this is already a last-resort fallback (tiers 1 and 2 both failed), and correct layout detection is worth the extra bytes.
Test plan
npm run lintcleanvitest run: same 14 pre-existing failures, zero new failuresSummary by CodeRabbit