fix(stake): #257 — add v17 devnet wrapper to the feature-gated devnet allowlist - #263
Conversation
…ist (dcccrypto#257) InitPool's percolator_program allowlist only recognized the legacy (pre-v17) PERCOLATOR_DEVNET address, not percolator-sdk's PROGRAM_IDS_V17.percolator ("69VUZ7a2BeXBTpRRManLamF5UWTaNR9B1hy5Se3cdXy9"), a separate devnet-only deployment from the v17 cutover (2026-06-24). Initializing a pool against the canonical v17 SDK config failed with InvalidPercolatorProgram. Note on scope, correcting dcccrypto#257's framing: the SDK's own comment states mainnet v17 addresses are "pending the mainnet cutover (Phase 7 gate)" -- there is no canonical v17 MAINNET address yet, so this is a devnet integration gap, not a live mainnet liveness failure as the issue described. It's also why the fix adds PERCOLATOR_V17_DEVNET inside the EXISTING "devnet" feature gate (alongside the legacy PERCOLATOR_DEVNET) rather than the issue's suggested unconditional addition to the mainnet check -- the existing devnet gating was added deliberately (N-3) because a compromised devnet deploy keypair has no legitimate mainnet use; an unconditional addition would have reintroduced exactly that regression for a second address. Verified the fix compiles and the full test suite passes both with and without --features devnet (CI only runs the default/no-devnet build, so this is the only way to confirm the gated branch isn't broken). Added a constants-drift test mirroring the existing test_percolator_program_allowlist_constants_match_nft pattern.
📝 WalkthroughWalkthroughAdds a devnet-gated ChangesPercolator wrapper allowlist
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Possibly related issues
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 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 `@tests/unit.rs`:
- Around line 937-956: The v17 devnet wrapper test only compares hardcoded
Pubkeys against each other, so it can miss drift from the SDK. Update
test_v17_devnet_wrapper_allowlist_constant_matches_sdk to assert the wrapper
value matches the SDK-exported source of truth (for example the percolator-sdk
PROGRAM_IDS_V17.percolator constant) instead of another literal, while keeping
the existing sanity checks that it differs from mainnet/legacy and is
non-default.
🪄 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: f6c67fda-2fb1-44e6-845a-ef512e7293d5
📒 Files selected for processing (2)
src/processor.rstests/unit.rs
| fn test_v17_devnet_wrapper_allowlist_constant_matches_sdk() { | ||
| // #257: percolator-sdk's PROGRAM_IDS_V17.percolator is a separate, newer | ||
| // devnet-only deployment from the legacy PERCOLATOR_DEVNET above (the SDK's | ||
| // own comment notes mainnet v17 addresses are "pending the mainnet cutover" — | ||
| // there is no canonical v17 MAINNET address yet, so only a devnet allowlist | ||
| // entry is correct here). This test encodes the expected value so drift is | ||
| // caught at test time, mirroring test_percolator_program_allowlist_constants_match_nft. | ||
| use solana_program::pubkey::Pubkey; | ||
|
|
||
| let mainnet: Pubkey = solana_program::pubkey!("ESa89R5Es3rJ5mnwGybVRG1GrNt9etP11Z5V2QWD4edv"); | ||
| let legacy_devnet: Pubkey = | ||
| solana_program::pubkey!("FxfD37s1AZTeWfFQps9Zpebi2dNQ9QSSDtfMKdbsfKrD"); | ||
| let v17_devnet: Pubkey = solana_program::pubkey!("69VUZ7a2BeXBTpRRManLamF5UWTaNR9B1hy5Se3cdXy9"); | ||
|
|
||
| assert_ne!(v17_devnet, mainnet, "v17 devnet wrapper must differ from mainnet"); | ||
| assert_ne!( | ||
| v17_devnet, legacy_devnet, | ||
| "v17 devnet wrapper must differ from the legacy (pre-v17) devnet wrapper" | ||
| ); | ||
| assert_ne!(v17_devnet, Pubkey::default(), "v17 devnet ID must not be zero"); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== Find SDK dependency and any direct use of PROGRAM_IDS_V17 =="
fd 'Cargo.toml|Cargo.lock' . -x sh -c 'echo "--- $1"; rg -n "percolator-sdk|PROGRAM_IDS_V17" "$1" || true' sh {}
echo
echo "== Inspect current test/production references =="
rg -n -C2 'PROGRAM_IDS_V17|69VUZ7a2BeXBTpRRManLamF5UWTaNR9B1hy5Se3cdXy9|PERCOLATOR_V17_DEVNET' src testsRepository: dcccrypto/percolator-stake
Length of output: 2387
Compare against the SDK-exported constant
These literals only prove the addresses differ; the test still passes if percolator-sdk changes PROGRAM_IDS_V17.percolator. Compare against the SDK constant or a shared source of truth instead of another hardcoded string.
🤖 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 `@tests/unit.rs` around lines 937 - 956, The v17 devnet wrapper test only
compares hardcoded Pubkeys against each other, so it can miss drift from the
SDK. Update test_v17_devnet_wrapper_allowlist_constant_matches_sdk to assert the
wrapper value matches the SDK-exported source of truth (for example the
percolator-sdk PROGRAM_IDS_V17.percolator constant) instead of another literal,
while keeping the existing sanity checks that it differs from mainnet/legacy and
is non-default.
Problem
InitPool'spercolator_programallowlist only recognized the legacy (pre-v17) devnet address (PERCOLATOR_DEVNET, gated behind thedevnetfeature). It did not recognizepercolator-sdk'sPROGRAM_IDS_V17.percolator(69VUZ7a2BeXBTpRRManLamF5UWTaNR9B1hy5Se3cdXy9), a separate devnet-only deployment from the v17 cutover (2026-06-24, per the SDK source). Initializing a pool against the canonical v17 SDK config fails withInvalidPercolatorProgram.Correcting the original issue's framing
I verified the SDK source (
percolator-sdk/src/config/program-ids.ts) before touching anything, since the issue's claim didn't fully match what's actually there:PROGRAM_IDS_V17has no devnet/mainnet split at all, it's devnet-only. So this is a devnet integration gap blocking v17 testing today, not "full staking/vault liveness failure" for a live mainnet deployment as the issue described — mainnet v17 doesn't exist yet to be blocked.let is_valid = *percolator_program.key == PERCOLATOR_MAINNET || *percolator_program.key == PERCOLATOR_V17;, unconditional, no feature gate) would have been a regression:PERCOLATOR_DEVNETwas deliberately moved behind thedevnetfeature flag in a recent PR (N-3 fix, fix(stake): gate PERCOLATOR_DEVNET in program allowlist behind devnet feature flag #235) specifically because "if the devnet deploy keypair is compromised an attacker can deploy a malicious binary at the devnet address on mainnet... and drain the vault." The same reasoning applies to this new devnet-only address — it has no legitimate use on mainnet either.Fix
Added
PERCOLATOR_V17_DEVNETalongside the existingPERCOLATOR_DEVNET, inside the same#[cfg(feature = "devnet")]gate, so it's available for devnet integration testing without ever compiling into a mainnet build.Proof of Fix
cargo build/cargo test --libpass both with and without--features devnet— CI (.github/workflows/ci.yml) only ever runs the default (no-devnet) build, so this is the only way to confirm the gated branch isn't broken by this change.test_v17_devnet_wrapper_allowlist_constant_matches_sdkintests/unit.rs, mirroring the existingtest_percolator_program_allowlist_constants_match_nftconstants-drift-detection pattern.cargo test --lib(142 tests) and non-SBF-dependent integration/proptest files (198 more tests, 340 total) pass with no regressions, in both feature configurations.Related
Closes #257
Summary by CodeRabbit