From 5e99b004cd911afe6668d9a3a609804e6510bf7f Mon Sep 17 00:00:00 2001 From: Orioye Blessing Esther <210155349+ayaoba24@users.noreply.github.com> Date: Tue, 30 Jun 2026 10:25:59 +0100 Subject: [PATCH 1/2] Update API_DOCUMENTATION.md --- API_DOCUMENTATION.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/API_DOCUMENTATION.md b/API_DOCUMENTATION.md index 8eb024c1..b0647a0a 100644 --- a/API_DOCUMENTATION.md +++ b/API_DOCUMENTATION.md @@ -1,5 +1,7 @@ # Predictify Hybrid Contract - API Documentation + + ## Overview This document provides a complete API reference for the Predictify Hybrid smart contract. It details all public modules, their purposes, and available functions organized by functional domain. From 492a8b3a29c1887ce96a21fe86ca972dd9233e34 Mon Sep 17 00:00:00 2001 From: ayaoba24 Date: Thu, 2 Jul 2026 08:57:36 +0100 Subject: [PATCH 2/2] feat: add TTL pressure preflight query to storage module --- contracts/predictify-hybrid/src/storage.rs | 51 +++++++++++++++++-- .../src/storage_layout_tests.rs | 41 +++++++++++++++ 2 files changed, 88 insertions(+), 4 deletions(-) diff --git a/contracts/predictify-hybrid/src/storage.rs b/contracts/predictify-hybrid/src/storage.rs index 589cb3c1..96e2ce33 100644 --- a/contracts/predictify-hybrid/src/storage.rs +++ b/contracts/predictify-hybrid/src/storage.rs @@ -57,6 +57,14 @@ enum StorageTtlTier { Archive, } +#[contracttype] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct StorageTtlPressure { + pub key: Val, + pub remaining_ledgers: u32, + pub recommended_bump: u32, +} + // ===== STORAGE OPTIMIZATION TYPES ===== /// Storage key variants for contracts/predictify-hybrid @@ -85,8 +93,8 @@ pub enum DataKey { /// Instance storage cache key for Market structs, keyed by market_id. /// Used by MarketReadCache in markets.rs. MarketCache(Symbol), - /// Nonce for admin override replay protection. - AdminOverrideNonce(Address), + /// Stores the state for multisig signer rotation cooldowns + MultisigRotationState, } /// Storage format version for migration tracking @@ -373,6 +381,41 @@ impl StorageOptimizer { Self::extend_persistent_ttl(env, key, desired_ttl_ledgers); } + /// Pre-flight query to check TTL pressure of keys + pub fn check_ttl_pressure(env: &Env, keys: Vec) -> Vec { + let max_ttl = env.storage().max_ttl(); + let mut pressures = alloc::vec::Vec::new(); + + for key in keys.iter() { + let mut remaining = None; + + if env.storage().persistent().has(&key) { + remaining = Some(env.storage().persistent().get_ttl(&key)); + } else if env.storage().temporary().has(&key) { + remaining = Some(env.storage().temporary().get_ttl(&key)); + } else if env.storage().instance().has(&key) { + remaining = Some(env.storage().instance().get_ttl()); + } + + if let Some(r) = remaining { + let bump = MARKET_TTL_LEDGERS.min(max_ttl); + pressures.push(StorageTtlPressure { + key: key.clone(), + remaining_ledgers: r, + recommended_bump: bump, + }); + } + } + + pressures.sort_by_key(|p| p.remaining_ledgers); + + let mut result = Vec::new(env); + for p in pressures { + result.push_back(p); + } + result + } + /// Compress market data for storage optimization pub fn compress_market_data(env: &Env, market: &Market) -> Result { // Create a simple compression by removing unnecessary fields and optimizing structure @@ -423,7 +466,7 @@ impl StorageOptimizer { MarketStateManager::remove_market(env, market_id); // Emit cleanup event - events::EventEmitter::emit_storage_cleanup_event( + crate::events::EventEmitter::emit_storage_cleanup_event( env, market_id, &String::from_str(env, "old_market_cleanup"), @@ -561,7 +604,7 @@ impl StorageOptimizer { Self::update_market_to_compressed(env, market_id, &compressed_market.market_id)?; // Emit optimization event - events::EventEmitter::emit_storage_optimization_event( + crate::events::EventEmitter::emit_storage_optimization_event( env, market_id, &String::from_str(env, "compression_applied"), diff --git a/contracts/predictify-hybrid/src/storage_layout_tests.rs b/contracts/predictify-hybrid/src/storage_layout_tests.rs index c5c77cb6..1f1428cc 100644 --- a/contracts/predictify-hybrid/src/storage_layout_tests.rs +++ b/contracts/predictify-hybrid/src/storage_layout_tests.rs @@ -887,3 +887,44 @@ fn test_demote_scratch_keys_auth_failure() { let _ = crate::storage::StorageMigration::demote_scratch_keys(&env, &market_id); }); } + +#[test] +fn test_check_ttl_pressure() { + let env = create_test_env(); + let contract_id = env.register(crate::PredictifyHybrid, ()); + + env.as_contract(&contract_id, || { + use soroban_sdk::IntoVal; + + let key1 = Symbol::new(&env, "TestKey1"); + let value1 = String::from_str(&env, "TestValue1"); + + let key2 = Symbol::new(&env, "TestKey2"); + let value2 = String::from_str(&env, "TestValue2"); + + env.storage().persistent().set(&key1, &value1); + env.storage().persistent().extend_ttl(&key1, 200, 200); + + env.storage().persistent().set(&key2, &value2); + env.storage().persistent().extend_ttl(&key2, 100, 100); + + let keys = vec![&env, key1.into_val(&env), key2.into_val(&env)]; + let pressures = StorageOptimizer::check_ttl_pressure(&env, keys); + + assert_eq!(pressures.len(), 2); + + let pressure0 = pressures.get(0).unwrap(); + let pressure1 = pressures.get(1).unwrap(); + + assert_eq!(pressure0.key, key2.into_val(&env)); + assert!(pressure0.remaining_ledgers <= 100); + + assert_eq!(pressure1.key, key1.into_val(&env)); + assert!(pressure1.remaining_ledgers <= 200); + assert!(pressure0.remaining_ledgers < pressure1.remaining_ledgers); + + let expected_bump = crate::storage::MARKET_TTL_LEDGERS.min(env.storage().max_ttl()); + assert_eq!(pressure0.recommended_bump, expected_bump); + assert_eq!(pressure1.recommended_bump, expected_bump); + }); +}