Skip to content
Merged
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
143 changes: 143 additions & 0 deletions contracts/predictify-hybrid/src/event_archive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,8 @@ mod tests {
use super::*;
use alloc::string::ToString;
use soroban_sdk::testutils::Address as _;
use soroban_sdk::Map;
use crate::types::{Market, MarketState, OracleConfig};

struct EventArchiveTest {
env: Env,
Expand Down Expand Up @@ -749,4 +751,145 @@ mod tests {
});
assert_eq!(entries.len(), 0);
}

#[test]
fn test_archive_max_length_market_id_no_panic() {
let test = EventArchiveTest::new();
let env = &test.env;
let contract_id = env.register(crate::PredictifyHybrid, ());

env.as_contract(&contract_id, || {
let max_length_id = Symbol::new(env, "a_very_long_market_id_32_chars__");

let market = Market {
admin: test.admin.clone(),
question: String::from_str(env, "Will BTC reach 100k?"),
outcomes: soroban_sdk::vec![env, String::from_str(env, "yes")],
end_time: env.ledger().timestamp() + 3600,
oracle_config: OracleConfig::none_sentinel(env),
has_fallback: false,
fallback_oracle_config: OracleConfig::none_sentinel(env),
resolution_timeout: 3600,
oracle_result: None,
votes: Map::new(env),
total_staked: 0,
dispute_stakes: Map::new(env),
stakes: Map::new(env),
claimed: Map::new(env),
winning_outcomes: None,
fee_collected: false,
state: MarketState::Resolved,
total_extension_days: 0,
max_extension_days: 30,
extension_history: soroban_sdk::vec![env],
category: None,
tags: soroban_sdk::vec![env],
min_pool_size: None,
bet_deadline: 0,
dispute_window_seconds: 3600,
winnings_swept: false,
};

let res = crate::storage::StorageOptimizer::archive_market_data(env, &max_length_id, &market);
assert!(res.is_ok());
});
}

#[test]
fn test_archive_repeated_same_ledger_works() {
let test = EventArchiveTest::new();
let env = &test.env;
let contract_id = env.register(crate::PredictifyHybrid, ());

env.as_contract(&contract_id, || {
let market_id = Symbol::new(env, "market_1");

let market = Market {
admin: test.admin.clone(),
question: String::from_str(env, "Will BTC reach 100k?"),
outcomes: soroban_sdk::vec![env, String::from_str(env, "yes")],
end_time: env.ledger().timestamp() + 3600,
oracle_config: OracleConfig::none_sentinel(env),
has_fallback: false,
fallback_oracle_config: OracleConfig::none_sentinel(env),
resolution_timeout: 3600,
oracle_result: None,
votes: Map::new(env),
total_staked: 0,
dispute_stakes: Map::new(env),
stakes: Map::new(env),
claimed: Map::new(env),
winning_outcomes: None,
fee_collected: false,
state: MarketState::Resolved,
total_extension_days: 0,
max_extension_days: 30,
extension_history: soroban_sdk::vec![env],
category: None,
tags: soroban_sdk::vec![env],
min_pool_size: None,
bet_deadline: 0,
dispute_window_seconds: 3600,
winnings_swept: false,
};

let res1 = crate::storage::StorageOptimizer::archive_market_data(env, &market_id, &market);
assert!(res1.is_ok());

let res2 = crate::storage::StorageOptimizer::archive_market_data(env, &market_id, &market);
assert!(res2.is_ok());
});
}

#[test]
fn test_archive_entries_are_retrievable() {
let test = EventArchiveTest::new();
let env = &test.env;
let contract_id = env.register(crate::PredictifyHybrid, ());

env.as_contract(&contract_id, || {
let market_id = Symbol::new(env, "retrievable_market");
let timestamp = env.ledger().timestamp();

let market = Market {
admin: test.admin.clone(),
question: String::from_str(env, "Will BTC reach 100k?"),
outcomes: soroban_sdk::vec![env, String::from_str(env, "yes")],
end_time: env.ledger().timestamp() + 3600,
oracle_config: OracleConfig::none_sentinel(env),
has_fallback: false,
fallback_oracle_config: OracleConfig::none_sentinel(env),
resolution_timeout: 3600,
oracle_result: None,
votes: Map::new(env),
total_staked: 0,
dispute_stakes: Map::new(env),
stakes: Map::new(env),
claimed: Map::new(env),
winning_outcomes: None,
fee_collected: false,
state: MarketState::Resolved,
total_extension_days: 0,
max_extension_days: 30,
extension_history: soroban_sdk::vec![env],
category: None,
tags: soroban_sdk::vec![env],
min_pool_size: None,
bet_deadline: 0,
dispute_window_seconds: 3600,
winnings_swept: false,
};

let res = crate::storage::StorageOptimizer::archive_market_data(env, &market_id, &market);
assert!(res.is_ok());

let key = crate::storage::DataKey::ArchivedMarket(market_id.clone(), timestamp);
let retrieved: Option<Market> = env.storage().persistent().get(&key);
assert!(retrieved.is_some());

let retrieved_market = retrieved.unwrap();
assert_eq!(retrieved_market.question, market.question);
assert_eq!(retrieved_market.admin, market.admin);
});
}
}
16 changes: 11 additions & 5 deletions contracts/predictify-hybrid/src/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,15 @@ enum StorageTtlTier {

// ===== STORAGE OPTIMIZATION TYPES =====

/// Storage key variants for contracts/predictify-hybrid
#[contracttype]
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum DataKey {
Whitelisted(Address),
Blacklisted(Address),
ArchivedMarket(Symbol, u64),
}

/// Storage format version for migration tracking
#[contracttype]
#[derive(Clone, Debug, Eq, PartialEq)]
Expand Down Expand Up @@ -684,12 +693,9 @@ impl StorageOptimizer {
}

/// Archive market data before deletion
fn archive_market_data(env: &Env, market_id: &Symbol, market: &Market) -> Result<(), Error> {
pub(crate) fn archive_market_data(env: &Env, market_id: &Symbol, market: &Market) -> Result<(), Error> {
// Store archived version with timestamp
let archive_key = Symbol::new(
env,
&format!("archive_{:?}_{}", market_id, env.ledger().timestamp()),
);
let archive_key = DataKey::ArchivedMarket(market_id.clone(), env.ledger().timestamp());
Self::set_persistent_with_ttl(
env,
&archive_key,
Expand Down
10 changes: 8 additions & 2 deletions contracts/predictify-hybrid/src/storage_layout_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -233,12 +233,18 @@ fn test_formatted_key_uniqueness() {
let compressed_key_1 = format!("compressed_{:?}", market_id_1);
let compressed_key_2 = format!("compressed_{:?}", market_id_2);
let compressed_ref_key_1 = format!("compressed_ref_{:?}", market_id_1);
let archive_key_1 = format!("archive_{:?}_1234567890", market_id_1);

// Verify formatted keys are unique
assert_ne!(compressed_key_1, compressed_key_2);
assert_ne!(compressed_key_1, compressed_ref_key_1);
assert_ne!(compressed_key_1, archive_key_1);

// Test ArchivedMarket DataKey uniqueness
let archive_key_1 = crate::storage::DataKey::ArchivedMarket(market_id_1.clone(), 1234567890);
let archive_key_2 = crate::storage::DataKey::ArchivedMarket(market_id_2.clone(), 1234567890);
let archive_key_3 = crate::storage::DataKey::ArchivedMarket(market_id_1.clone(), 9876543210);

assert_ne!(archive_key_1, archive_key_2);
assert_ne!(archive_key_1, archive_key_3);
}

// ===== STORAGE KEY NAMESPACE TESTS =====
Expand Down
Loading