From 2febadc8838d7278e297e81822fbd5e4ebde798b Mon Sep 17 00:00:00 2001 From: r4bbit <445106+0x-r4bbit@users.noreply.github.com> Date: Fri, 19 Jun 2026 14:38:01 +0200 Subject: [PATCH] refactor(pda): use descriptive string seeds for PDA derivation Replace the hardcoded numeric byte-stream seeds ([0; 32], [1; 32], ...) used for domain separation in PDA derivation with descriptive byte-string constants, mirroring the AMM config account's existing b"CONFIG" seed. amm: [0; 32] -> b"LIQUIDITY_TOKEN" [1; 32] -> b"LP_LOCK_HOLDING" stablecoin: [0; 32] -> b"POSITION" [1; 32] -> b"POSITION_VAULT" twap_oracle: [2; 32] -> b"PRICE_OBSERVATIONS" [3; 32] -> b"ORACLE_PRICE_ACCOUNT" [4; 32] -> b"CURRENT_TICK_ACCOUNT" Since the seeds are now variable-length, each compute_*_pda_seed function builds its hash input with a Vec and extend_from_slice instead of a fixed-size buffer with offset writes. Closes #146 --- programs/amm/core/src/lib.rs | 22 ++++++++---------- programs/stablecoin/core/src/lib.rs | 20 ++++++++-------- programs/twap_oracle/core/src/lib.rs | 34 ++++++++++++++-------------- 3 files changed, 38 insertions(+), 38 deletions(-) diff --git a/programs/amm/core/src/lib.rs b/programs/amm/core/src/lib.rs index 324e8ef..3532522 100644 --- a/programs/amm/core/src/lib.rs +++ b/programs/amm/core/src/lib.rs @@ -8,10 +8,10 @@ use nssa_core::{ use serde::{Deserialize, Serialize}; use spel_framework_macros::account_type; -// These stable seed bytes are part of the PDA derivation scheme and must stay unchanged for -// compatibility. -const LIQUIDITY_TOKEN_PDA_SEED: [u8; 32] = [0; 32]; -const LP_LOCK_HOLDING_PDA_SEED: [u8; 32] = [1; 32]; +// These stable domain-separation tags are part of the PDA derivation scheme and must stay +// unchanged for address compatibility. +const LIQUIDITY_TOKEN_PDA_SEED: &[u8] = b"LIQUIDITY_TOKEN"; +const LP_LOCK_HOLDING_PDA_SEED: &[u8] = b"LP_LOCK_HOLDING"; /// AMM Program Instruction. #[derive(Serialize, Deserialize)] @@ -320,10 +320,9 @@ pub fn compute_liquidity_token_pda(amm_program_id: ProgramId, pool_id: AccountId pub fn compute_liquidity_token_pda_seed(pool_id: AccountId) -> PdaSeed { use risc0_zkvm::sha::{Impl, Sha256}; - let mut bytes = [0; 64]; - let (pool_bytes, seed_bytes) = bytes.split_at_mut(32); - pool_bytes.copy_from_slice(&pool_id.to_bytes()); - seed_bytes.copy_from_slice(&LIQUIDITY_TOKEN_PDA_SEED); + let mut bytes = Vec::new(); + bytes.extend_from_slice(&pool_id.to_bytes()); + bytes.extend_from_slice(LIQUIDITY_TOKEN_PDA_SEED); PdaSeed::new( Impl::hash_bytes(&bytes) @@ -340,10 +339,9 @@ pub fn compute_lp_lock_holding_pda(amm_program_id: ProgramId, pool_id: AccountId pub fn compute_lp_lock_holding_pda_seed(pool_id: AccountId) -> PdaSeed { use risc0_zkvm::sha::{Impl, Sha256}; - let mut bytes = [0; 64]; - let (pool_bytes, seed_bytes) = bytes.split_at_mut(32); - pool_bytes.copy_from_slice(&pool_id.to_bytes()); - seed_bytes.copy_from_slice(&LP_LOCK_HOLDING_PDA_SEED); + let mut bytes = Vec::new(); + bytes.extend_from_slice(&pool_id.to_bytes()); + bytes.extend_from_slice(LP_LOCK_HOLDING_PDA_SEED); PdaSeed::new( Impl::hash_bytes(&bytes) diff --git a/programs/stablecoin/core/src/lib.rs b/programs/stablecoin/core/src/lib.rs index b5f51ac..d768069 100644 --- a/programs/stablecoin/core/src/lib.rs +++ b/programs/stablecoin/core/src/lib.rs @@ -8,8 +8,10 @@ use nssa_core::{ use serde::{Deserialize, Serialize}; use spel_framework_macros::account_type; -const POSITION_PDA_DOMAIN: [u8; 32] = [0; 32]; -const POSITION_VAULT_PDA_DOMAIN: [u8; 32] = [1; 32]; +// Stable domain-separation tags for the position PDAs; these must stay unchanged for address +// compatibility. +const POSITION_PDA_DOMAIN: &[u8] = b"POSITION"; +const POSITION_VAULT_PDA_DOMAIN: &[u8] = b"POSITION_VAULT"; /// Stablecoin Program Instruction. #[derive(Debug, Serialize, Deserialize)] @@ -122,10 +124,10 @@ pub fn compute_position_pda_seed( ) -> PdaSeed { use risc0_zkvm::sha::{Impl, Sha256 as _}; - let mut bytes = [0u8; 96]; - bytes[0..32].copy_from_slice(&owner_id.to_bytes()); - bytes[32..64].copy_from_slice(&collateral_definition_id.to_bytes()); - bytes[64..96].copy_from_slice(&POSITION_PDA_DOMAIN); + let mut bytes = Vec::new(); + bytes.extend_from_slice(&owner_id.to_bytes()); + bytes.extend_from_slice(&collateral_definition_id.to_bytes()); + bytes.extend_from_slice(POSITION_PDA_DOMAIN); let mut out = [0u8; 32]; out.copy_from_slice(Impl::hash_bytes(&bytes).as_bytes()); @@ -151,9 +153,9 @@ pub fn compute_position_pda( pub fn compute_position_vault_pda_seed(position_id: AccountId) -> PdaSeed { use risc0_zkvm::sha::{Impl, Sha256 as _}; - let mut bytes = [0u8; 64]; - bytes[0..32].copy_from_slice(&position_id.to_bytes()); - bytes[32..64].copy_from_slice(&POSITION_VAULT_PDA_DOMAIN); + let mut bytes = Vec::new(); + bytes.extend_from_slice(&position_id.to_bytes()); + bytes.extend_from_slice(POSITION_VAULT_PDA_DOMAIN); let mut out = [0u8; 32]; out.copy_from_slice(Impl::hash_bytes(&bytes).as_bytes()); diff --git a/programs/twap_oracle/core/src/lib.rs b/programs/twap_oracle/core/src/lib.rs index 57e2da6..d4c5a9b 100644 --- a/programs/twap_oracle/core/src/lib.rs +++ b/programs/twap_oracle/core/src/lib.rs @@ -211,7 +211,7 @@ impl From<&PriceObservations> for Data { // PDA helpers // ────────────────────────────────────────────────────────────────────────────── -const PRICE_OBSERVATIONS_PDA_SEED: [u8; 32] = [2; 32]; +const PRICE_OBSERVATIONS_PDA_SEED: &[u8] = b"PRICE_OBSERVATIONS"; /// Derives the [`AccountId`] for a price source's [`PriceObservations`] PDA. /// @@ -232,7 +232,7 @@ pub fn compute_price_observations_pda( /// Derives the [`PdaSeed`] for a price source's [`PriceObservations`]. /// /// Hash input: `price_source_id (32 bytes) || window_duration_le (8 bytes) || -/// PRICE_OBSERVATIONS_PDA_SEED (32 bytes)`. +/// PRICE_OBSERVATIONS_PDA_SEED`. #[must_use] pub fn compute_price_observations_pda_seed( price_source_id: AccountId, @@ -240,10 +240,10 @@ pub fn compute_price_observations_pda_seed( ) -> PdaSeed { use risc0_zkvm::sha::{Impl, Sha256}; - let mut bytes = [0u8; 72]; - bytes[..32].copy_from_slice(&price_source_id.to_bytes()); - bytes[32..40].copy_from_slice(&window_duration.to_le_bytes()); - bytes[40..72].copy_from_slice(&PRICE_OBSERVATIONS_PDA_SEED); + let mut bytes = Vec::new(); + bytes.extend_from_slice(&price_source_id.to_bytes()); + bytes.extend_from_slice(&window_duration.to_le_bytes()); + bytes.extend_from_slice(PRICE_OBSERVATIONS_PDA_SEED); PdaSeed::new( Impl::hash_bytes(&bytes) @@ -253,7 +253,7 @@ pub fn compute_price_observations_pda_seed( ) } -const ORACLE_PRICE_ACCOUNT_PDA_SEED: [u8; 32] = [3; 32]; +const ORACLE_PRICE_ACCOUNT_PDA_SEED: &[u8] = b"ORACLE_PRICE_ACCOUNT"; /// Derives the [`AccountId`] for a price source's [`OraclePriceAccount`] PDA. /// @@ -274,7 +274,7 @@ pub fn compute_oracle_price_account_pda( /// Derives the [`PdaSeed`] for a price source's [`OraclePriceAccount`]. /// /// Hash input: `price_source_id (32 bytes) || window_duration_le (8 bytes) || -/// ORACLE_PRICE_ACCOUNT_PDA_SEED (32 bytes)`. +/// ORACLE_PRICE_ACCOUNT_PDA_SEED`. #[must_use] pub fn compute_oracle_price_account_pda_seed( price_source_id: AccountId, @@ -282,10 +282,10 @@ pub fn compute_oracle_price_account_pda_seed( ) -> PdaSeed { use risc0_zkvm::sha::{Impl, Sha256}; - let mut bytes = [0u8; 72]; - bytes[..32].copy_from_slice(&price_source_id.to_bytes()); - bytes[32..40].copy_from_slice(&window_duration.to_le_bytes()); - bytes[40..72].copy_from_slice(&ORACLE_PRICE_ACCOUNT_PDA_SEED); + let mut bytes = Vec::new(); + bytes.extend_from_slice(&price_source_id.to_bytes()); + bytes.extend_from_slice(&window_duration.to_le_bytes()); + bytes.extend_from_slice(ORACLE_PRICE_ACCOUNT_PDA_SEED); PdaSeed::new( Impl::hash_bytes(&bytes) @@ -459,7 +459,7 @@ impl From<&CurrentTickAccount> for Data { } } -const CURRENT_TICK_ACCOUNT_PDA_SEED: [u8; 32] = [4; 32]; +const CURRENT_TICK_ACCOUNT_PDA_SEED: &[u8] = b"CURRENT_TICK_ACCOUNT"; /// Derives the [`AccountId`] for a price source's [`CurrentTickAccount`] PDA. #[must_use] @@ -475,14 +475,14 @@ pub fn compute_current_tick_account_pda( /// Derives the [`PdaSeed`] for a price source's [`CurrentTickAccount`]. /// -/// Hash input: `price_source_id (32 bytes) || CURRENT_TICK_ACCOUNT_PDA_SEED (32 bytes)`. +/// Hash input: `price_source_id (32 bytes) || CURRENT_TICK_ACCOUNT_PDA_SEED`. #[must_use] pub fn compute_current_tick_account_pda_seed(price_source_id: AccountId) -> PdaSeed { use risc0_zkvm::sha::{Impl, Sha256}; - let mut bytes = [0u8; 64]; - bytes[..32].copy_from_slice(&price_source_id.to_bytes()); - bytes[32..64].copy_from_slice(&CURRENT_TICK_ACCOUNT_PDA_SEED); + let mut bytes = Vec::new(); + bytes.extend_from_slice(&price_source_id.to_bytes()); + bytes.extend_from_slice(CURRENT_TICK_ACCOUNT_PDA_SEED); PdaSeed::new( Impl::hash_bytes(&bytes)