From 72b81f15168e6196ad64938a88658127cb186d5b Mon Sep 17 00:00:00 2001 From: Morenikeoa Date: Sat, 27 Jun 2026 08:49:44 +0100 Subject: [PATCH] fix(stake): add v17 devnet wrapper to the feature-gated devnet allowlist (#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 #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. --- src/processor.rs | 15 ++++++++++++++- tests/unit.rs | 23 +++++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/src/processor.rs b/src/processor.rs index b7c9d38..26ebc1a 100644 --- a/src/processor.rs +++ b/src/processor.rs @@ -444,15 +444,28 @@ fn process_init_pool( // pointing to it, and drain the vault via FlushToInsurance PDA-signer propagation. // The devnet program ID has no legitimate use on mainnet; gate it with the // "devnet" feature flag so it only compiles in when explicitly requested. + // + // #257: the legacy PERCOLATOR_DEVNET address above predates the v17 wrapper cutover. + // percolator-sdk's PROGRAM_IDS_V17.percolator ("69VUZ7a2BeXBTpRRManLamF5UWTaNR9B1hy5Se3cdXy9") + // is a SEPARATE, newer devnet-only deployment (SDK comment: "Mainnet addresses are + // pending the mainnet cutover (Phase 7 gate)" — there is no canonical v17 MAINNET + // address yet). Add it alongside, not in place of, the legacy devnet address, and + // keep it under the SAME "devnet" feature gate as PERCOLATOR_DEVNET for the same + // N-3 reason: it has no legitimate use on mainnet either. { const PERCOLATOR_MAINNET: Pubkey = solana_program::pubkey!("ESa89R5Es3rJ5mnwGybVRG1GrNt9etP11Z5V2QWD4edv"); #[cfg(feature = "devnet")] const PERCOLATOR_DEVNET: Pubkey = solana_program::pubkey!("FxfD37s1AZTeWfFQps9Zpebi2dNQ9QSSDtfMKdbsfKrD"); + #[cfg(feature = "devnet")] + const PERCOLATOR_V17_DEVNET: Pubkey = + solana_program::pubkey!("69VUZ7a2BeXBTpRRManLamF5UWTaNR9B1hy5Se3cdXy9"); let is_valid = *percolator_program.key == PERCOLATOR_MAINNET; #[cfg(feature = "devnet")] - let is_valid = is_valid || *percolator_program.key == PERCOLATOR_DEVNET; + let is_valid = is_valid + || *percolator_program.key == PERCOLATOR_DEVNET + || *percolator_program.key == PERCOLATOR_V17_DEVNET; if !is_valid { msg!( "Error: invalid percolator program {}", diff --git a/tests/unit.rs b/tests/unit.rs index 7aad44e..705ec7d 100644 --- a/tests/unit.rs +++ b/tests/unit.rs @@ -933,6 +933,29 @@ fn test_percolator_program_allowlist_constants_match_nft() { assert_ne!(devnet, Pubkey::default(), "devnet ID must not be zero"); } +#[test] +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"); +} + #[test] fn test_pool_stores_percolator_program() { // Verify that a StakePool's percolator_program field is a full 32-byte pubkey.