fix(discovery): remove stale isV2 branch misrouting V12_15/17/19 in parseEngineLight - #339
Conversation
…arseEngineLight parseEngineLight's isV2 branch (gated on layout.version === 2) used hardcoded fixed offsets written for an old "V2 BPF intermediate" layout (e.g. currentSlot at a fixed base+352). That same version: 2 discriminant is also set by buildLayoutV12_15/17/19 (V12_19 inherits it by spreading V12_17's base layout) — an unrelated reuse of the same enum value — which meant V12_15/17/19 (the currently-deployed mainnet tier line) were silently routed through this stale branch's wrong offsets instead of their own correct per-field offsets. Verified directly: V12_19's real engineCurrentSlotOff is 200, not 352. This affected every field parseEngineLight returns for V12_15/17/19, not just one. parseEngineLight backs discoverMarkets() and getMarketsByAddress(), the SDK's primary market-listing entry points. The layout-driven branch immediately below the removed code already documents that it's meant to cover V_ADL, V12_1, V12_15, V12_17, V12_19, V1M, V1M2, V_SETDEXPOOL and any future layout — the isV2 branch was dead weight left over from a prior refactor that was never cleaned up. While moving V12_15/17/19 onto the layout-driven branch, also added the engineFundingIndexOff >= 0 guard that the heavy parser (slab.ts parseEngine) already has — V12_15/17/19 set this offset to -1 (the field doesn't exist in those engine structs), and the unguarded read would otherwise read 16 bytes starting one byte before the engine region. Exported parseEngineLight (was internal) so it can be unit tested directly, and added two regression tests for V12_19 proving (1) the real per-field offset is used instead of the dead branch's hardcoded one, and (2) fundingIndexQpbE6 is 0n instead of an off-by-one read. Both tests fail against the prior code (parseEngineLight not exported) and pass against this fix. Full suite: 853 passed | 31 skipped (882 -> 884, +2 new tests).
📝 WalkthroughWalkthroughThis PR exports ChangesparseEngineLight offset handling
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related issues
Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ 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.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/solana/discovery.ts`:
- Around line 399-404: The heavy parser in discovery should guard
`lastFundingSlot` the same way as `fundingIndexQpbE6`, because
`engineLastFundingSlotOff` can be `-1` when the field is absent. Update the
object construction in `src/solana/discovery.ts` so `lastFundingSlot` returns
`0n` when `engineLastFundingSlotOff < 0`, otherwise reads with `readU64LE`,
matching the existing offset-sentinel handling used by the other parser fields.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: c4ebbf02-70ae-449d-81ed-9109eef80c9a
📒 Files selected for processing (2)
src/solana/discovery.tstest/drift-check.test.ts
| fundingIndexQpbE6: l.engineFundingIndexOff >= 0 | ||
| ? ((l.engineLastFundingSlotOff >= 0 && l.engineLastFundingSlotOff - l.engineFundingIndexOff === 8) | ||
| ? BigInt(readI64LE(data, base + l.engineFundingIndexOff)) | ||
| : readI128LE(data, base + l.engineFundingIndexOff)) | ||
| : 0n, | ||
| lastFundingSlot: readU64LE(data, base + l.engineLastFundingSlotOff), |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Guard lastFundingSlot when its offset is absent.
Line 404 still reads engineLastFundingSlotOff unconditionally. Since slab offsets use -1 for absent fields and the heavy parser returns 0n in that case, mirror that guard here to avoid another base + (-1) read.
Proposed fix
- lastFundingSlot: readU64LE(data, base + l.engineLastFundingSlotOff),
+ lastFundingSlot: l.engineLastFundingSlotOff >= 0
+ ? readU64LE(data, base + l.engineLastFundingSlotOff)
+ : 0n,📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| fundingIndexQpbE6: l.engineFundingIndexOff >= 0 | |
| ? ((l.engineLastFundingSlotOff >= 0 && l.engineLastFundingSlotOff - l.engineFundingIndexOff === 8) | |
| ? BigInt(readI64LE(data, base + l.engineFundingIndexOff)) | |
| : readI128LE(data, base + l.engineFundingIndexOff)) | |
| : 0n, | |
| lastFundingSlot: readU64LE(data, base + l.engineLastFundingSlotOff), | |
| fundingIndexQpbE6: l.engineFundingIndexOff >= 0 | |
| ? ((l.engineLastFundingSlotOff >= 0 && l.engineLastFundingSlotOff - l.engineFundingIndexOff === 8) | |
| ? BigInt(readI64LE(data, base + l.engineFundingIndexOff)) | |
| : readI128LE(data, base + l.engineFundingIndexOff)) | |
| : 0n, | |
| lastFundingSlot: l.engineLastFundingSlotOff >= 0 | |
| ? readU64LE(data, base + l.engineLastFundingSlotOff) | |
| : 0n, |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/solana/discovery.ts` around lines 399 - 404, The heavy parser in
discovery should guard `lastFundingSlot` the same way as `fundingIndexQpbE6`,
because `engineLastFundingSlotOff` can be `-1` when the field is absent. Update
the object construction in `src/solana/discovery.ts` so `lastFundingSlot`
returns `0n` when `engineLastFundingSlotOff < 0`, otherwise reads with
`readU64LE`, matching the existing offset-sentinel handling used by the other
parser fields.
Summary
parseEngineLight'sisV2branch (gated onlayout.version === 2) used hardcoded fixed offsets written for an old "V2 BPF intermediate" layout (e.g.currentSlotat a fixedbase+352). That sameversion: 2discriminant is also set bybuildLayoutV12_15/17/19(V12_19 inherits it by spreading V12_17's base layout) — an unrelated reuse of the same enum value — which meant V12_15/17/19 (the currently-deployed mainnet tier line) were silently routed through this stale branch's wrong offsets instead of their own correct per-field offsets.Verified directly: V12_19's real
engineCurrentSlotOffis200, not352.This affected every field
parseEngineLightreturns for V12_15/17/19, not just one.parseEngineLightbacksdiscoverMarkets()andgetMarketsByAddress(), the SDK's primary market-listing entry points.The layout-driven branch immediately below the removed code already documents that it's meant to cover
V_ADL, V12_1, V12_15, V12_17, V12_19, V1M, V1M2, V_SETDEXPOOLand any future layout — theisV2branch was dead weight left over from a prior refactor that was never cleaned up.While moving V12_15/17/19 onto the layout-driven branch, also added the
engineFundingIndexOff >= 0guard that the heavy parser (slab.tsparseEngine) already has — V12_15/17/19 set this offset to-1(the field doesn't exist in those engine structs), and the unguarded read would otherwise read 16 bytes starting one byte before the engine region.Exported
parseEngineLight(was internal) so it can be unit tested directly.Test plan
test/drift-check.test.ts:200) is used instead of the dead branch's hardcoded one (352), via a planted decoy value at the old offset.fundingIndexQpbE6is0ninstead of an off-by-one read, via a planted marker byte atengineOff - 1.parseEngineLightnot exported / wrong offsets used) and pass against this fix.Summary by CodeRabbit
Bug Fixes
Tests