Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions API_DOCUMENTATION.md
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
51 changes: 47 additions & 4 deletions contracts/predictify-hybrid/src/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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<Val>) -> Vec<StorageTtlPressure> {
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<CompressedMarket, Error> {
// Create a simple compression by removing unnecessary fields and optimizing structure
Expand Down Expand Up @@ -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"),
Expand Down Expand Up @@ -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"),
Expand Down
41 changes: 41 additions & 0 deletions contracts/predictify-hybrid/src/storage_layout_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});
}
Loading