From 1c2affc3bc49f17d0c8ca043579a6d40c7e4aff1 Mon Sep 17 00:00:00 2001 From: JoyLight00 Date: Sun, 29 Mar 2026 19:46:55 +0100 Subject: [PATCH] feat(contract): implement reputation tests and LP token calculator --- contract/src/leaderboard.rs | 15 +- contract/src/liquidity.rs | 49 ++++++ contract/src/reputation.rs | 236 +------------------------- contract/src/season.rs | 6 +- contract/src/season_tests.rs | 5 +- contract/src/ttl.rs | 1 - contract/tests/escrow_tests.rs | 15 +- contract/tests/oracle_tests.rs | 11 +- contract/tests/reputation_tests.rs | 260 +++++++++++++++++++++++++++++ 9 files changed, 342 insertions(+), 256 deletions(-) create mode 100644 contract/tests/reputation_tests.rs diff --git a/contract/src/leaderboard.rs b/contract/src/leaderboard.rs index a3e6c469..f77b75a1 100644 --- a/contract/src/leaderboard.rs +++ b/contract/src/leaderboard.rs @@ -1,6 +1,6 @@ -use soroban_sdk::{Address, Env, Vec}; -use crate::storage_types::{DataKey, LeaderboardSnapshot, Season, UserProfile}; use crate::errors::InsightArenaError; +use crate::storage_types::{DataKey, LeaderboardSnapshot, Season, UserProfile}; +use soroban_sdk::{Address, Env, Vec}; /// `stake_bonus = floor(stake_xlm / 10)` → `floor(stake_stroops / 10^8 stroops)`. const STROOPS_PER_STAKE_POINT: i128 = 100_000_000; @@ -8,7 +8,10 @@ const STROOPS_PER_STAKE_POINT: i128 = 100_000_000; // --- Storage & Data Access --- /// Fetch a leaderboard snapshot for a specific season. -pub fn get_leaderboard(env: &Env, season_id: u32) -> Result { +pub fn get_leaderboard( + env: &Env, + season_id: u32, +) -> Result { let key = DataKey::Leaderboard(season_id); env.storage() .persistent() @@ -39,7 +42,7 @@ pub fn calculate_points(stake_amount: i128, correct: u32, total: u32) -> u32 { let sum = 100_i128.saturating_add(stake_bonus); let numer = sum.saturating_mul(correct).saturating_mul(2_i128); let res = numer / total; - + if res < 0 { return 0; } @@ -147,9 +150,9 @@ mod leaderboard_tests { #[test] fn get_user_season_points_unknown_season_and_user_returns_zero() { + use crate::InsightArenaContract; use soroban_sdk::testutils::Address as _; use soroban_sdk::{Address, Env}; - use crate::InsightArenaContract; let env = Env::default(); let contract_id = env.register(InsightArenaContract, ()); @@ -159,4 +162,4 @@ mod leaderboard_tests { }); assert_eq!(points, 0); } -} \ No newline at end of file +} diff --git a/contract/src/liquidity.rs b/contract/src/liquidity.rs index 7fe44594..c115e1a9 100644 --- a/contract/src/liquidity.rs +++ b/contract/src/liquidity.rs @@ -55,6 +55,31 @@ pub fn calculate_swap_output( // ── Liquidity Management ────────────────────────────────────────────────────── +/// Calculate LP tokens to mint for a deposit +pub fn calculate_lp_tokens( + deposit_amount: i128, + total_liquidity: i128, + total_lp_supply: i128, +) -> Result { + if deposit_amount <= 0 { + return Err(InsightArenaError::InvalidInput); + } + + // First deposit: mint tokens equal to deposit + if total_lp_supply == 0 || total_liquidity == 0 { + return Ok(deposit_amount); + } + + // Subsequent deposits: mint proportionally + let lp_tokens = deposit_amount + .checked_mul(total_lp_supply) + .ok_or(InsightArenaError::Overflow)? + .checked_div(total_liquidity) + .ok_or(InsightArenaError::Overflow)?; + + Ok(lp_tokens) +} + // TODO: add_liquidity // TODO: remove_liquidity @@ -98,4 +123,28 @@ mod tests { let result = calculate_swap_output(i128::MAX, 1000, 1000, 30); assert_eq!(result, Err(InsightArenaError::Overflow)); } + + #[test] + fn test_calculate_lp_tokens_first_deposit() { + // Deposit: 1000, Liquidity: 0, Supply: 0 → Expected: 1000 + assert_eq!(calculate_lp_tokens(1000, 0, 0), Ok(1000)); + } + + #[test] + fn test_calculate_lp_tokens_second_deposit_equal() { + // Deposit: 1000, Liquidity: 1000, Supply: 1000 → Expected: 1000 + assert_eq!(calculate_lp_tokens(1000, 1000, 1000), Ok(1000)); + } + + #[test] + fn test_calculate_lp_tokens_second_deposit_half() { + // Deposit: 500, Liquidity: 1000, Supply: 1000 → Expected: 500 + assert_eq!(calculate_lp_tokens(500, 1000, 1000), Ok(500)); + } + + #[test] + fn test_calculate_lp_tokens_second_deposit_double() { + // Deposit: 2000, Liquidity: 1000, Supply: 1000 → Expected: 2000 + assert_eq!(calculate_lp_tokens(2000, 1000, 1000), Ok(2000)); + } } diff --git a/contract/src/reputation.rs b/contract/src/reputation.rs index 0a6a0383..80b0e037 100644 --- a/contract/src/reputation.rs +++ b/contract/src/reputation.rs @@ -87,238 +87,4 @@ pub fn get_creator_stats(env: Env, creator: Address) -> Result Address { - let token_admin = Address::generate(env); - env.register_stellar_asset_contract_v2(token_admin) - .address() - } - - fn deploy(env: &Env) -> (InsightArenaContractClient<'_>, Address, Address) { - let id = env.register(InsightArenaContract, ()); - let client = InsightArenaContractClient::new(env, &id); - let admin = Address::generate(env); - let oracle = Address::generate(env); - let xlm_token = register_token(env); - env.mock_all_auths(); - client.initialize(&admin, &oracle, &200_u32, &xlm_token); - (client, admin, oracle) - } - - fn default_params(env: &Env) -> CreateMarketParams { - let now = env.ledger().timestamp(); - CreateMarketParams { - title: String::from_str(env, "Test market"), - description: String::from_str(env, "desc"), - category: Symbol::new(env, "Sports"), - outcomes: vec![env, symbol_short!("yes"), symbol_short!("no")], - end_time: now + 1000, - resolution_time: now + 2000, - dispute_window: 86_400, - creator_fee_bps: 100, - min_stake: 10_000_000, - max_stake: 100_000_000, - is_public: true, - } - } - - // ── Pure formula tests ──────────────────────────────────────────────────── - - #[test] - fn reputation_zero_for_new_creator() { - let stats = CreatorStats { - markets_created: 0, - markets_resolved: 0, - average_participant_count: 0, - dispute_count: 0, - reputation_score: 0, - }; - assert_eq!(calculate_creator_reputation(&stats), 0); - } - - #[test] - fn reputation_perfect_score_no_disputes() { - // 10/10 resolved, 100 avg participants → 600 + 200 - 0 = 800 - let stats = CreatorStats { - markets_created: 10, - markets_resolved: 10, - average_participant_count: 100, - dispute_count: 0, - reputation_score: 0, - }; - assert_eq!(calculate_creator_reputation(&stats), 800); - } - - #[test] - fn reputation_clamped_to_1000() { - let stats = CreatorStats { - markets_created: 1, - markets_resolved: 1, - average_participant_count: 300, // bonus capped at 200 - dispute_count: 0, - reputation_score: 0, - }; - // 600 + 200 = 800 - assert_eq!(calculate_creator_reputation(&stats), 800); - } - - #[test] - fn reputation_dispute_penalty_capped_at_200() { - // 10 * 50 = 500, capped at 200 → 600 + 0 - 200 = 400 - let stats = CreatorStats { - markets_created: 10, - markets_resolved: 10, - average_participant_count: 0, - dispute_count: 10, - reputation_score: 0, - }; - assert_eq!(calculate_creator_reputation(&stats), 400); - } - - #[test] - fn reputation_never_underflows() { - // 0 resolved, max disputes → saturating_sub → 0 - let stats = CreatorStats { - markets_created: 10, - markets_resolved: 0, - average_participant_count: 0, - dispute_count: 100, - reputation_score: 0, - }; - assert_eq!(calculate_creator_reputation(&stats), 0); - } - - #[test] - fn reputation_partial_resolution() { - // 5/10 * 600 = 300, 10 * 2 = 20, 1 * 50 = 50 → 270 - let stats = CreatorStats { - markets_created: 10, - markets_resolved: 5, - average_participant_count: 10, - dispute_count: 1, - reputation_score: 0, - }; - assert_eq!(calculate_creator_reputation(&stats), 270); - } - - #[test] - fn reputation_participation_bonus_capped_at_200() { - let stats = CreatorStats { - markets_created: 1, - markets_resolved: 1, - average_participant_count: 200, // 200 * 2 = 400, capped at 200 - dispute_count: 0, - reputation_score: 0, - }; - assert_eq!(calculate_creator_reputation(&stats), 800); - } - - // ── Integration tests ───────────────────────────────────────────────────── - - #[test] - fn get_creator_stats_returns_default_for_unknown_creator() { - let env = Env::default(); - env.mock_all_auths(); - let (client, _, _) = deploy(&env); - let unknown = Address::generate(&env); - - let stats = client.get_creator_stats(&unknown); - assert_eq!(stats.markets_created, 0); - assert_eq!(stats.markets_resolved, 0); - assert_eq!(stats.reputation_score, 0); - } - - #[test] - fn stats_updated_on_market_creation() { - let env = Env::default(); - env.mock_all_auths(); - let (client, _, _) = deploy(&env); - let creator = Address::generate(&env); - - client.create_market(&creator, &default_params(&env)); - - let stats = client.get_creator_stats(&creator); - assert_eq!(stats.markets_created, 1); - assert_eq!(stats.markets_resolved, 0); - } - - #[test] - fn stats_updated_on_market_resolution() { - let env = Env::default(); - env.mock_all_auths(); - let (client, _, oracle) = deploy(&env); - let creator = Address::generate(&env); - - let id = client.create_market(&creator, &default_params(&env)); - env.ledger().set_timestamp(env.ledger().timestamp() + 2000); - client.resolve_market(&oracle, &id, &symbol_short!("yes")); - - let stats = client.get_creator_stats(&creator); - assert_eq!(stats.markets_created, 1); - assert_eq!(stats.markets_resolved, 1); - } - - #[test] - fn stats_accumulate_across_multiple_markets() { - let env = Env::default(); - env.mock_all_auths(); - let (client, _, oracle) = deploy(&env); - let creator = Address::generate(&env); - - let id1 = client.create_market(&creator, &default_params(&env)); - let id2 = client.create_market(&creator, &default_params(&env)); - - let stats = client.get_creator_stats(&creator); - assert_eq!(stats.markets_created, 2); - - env.ledger().set_timestamp(env.ledger().timestamp() + 2000); - client.resolve_market(&oracle, &id1, &symbol_short!("yes")); - client.resolve_market(&oracle, &id2, &symbol_short!("no")); - - let stats = client.get_creator_stats(&creator); - assert_eq!(stats.markets_resolved, 2); - } - - #[test] - fn reputation_score_stored_in_stats() { - let env = Env::default(); - env.mock_all_auths(); - let (client, _, oracle) = deploy(&env); - let creator = Address::generate(&env); - - let id = client.create_market(&creator, &default_params(&env)); - env.ledger().set_timestamp(env.ledger().timestamp() + 2000); - client.resolve_market(&oracle, &id, &symbol_short!("yes")); - - let stats = client.get_creator_stats(&creator); - // 1/1 resolved = 600, 0 participants, 0 disputes → 600 - assert_eq!(stats.reputation_score, 600); - } - - #[test] - fn reputation_score_always_in_range() { - let env = Env::default(); - env.mock_all_auths(); - let (client, _, oracle) = deploy(&env); - let creator = Address::generate(&env); - - for _ in 0..3 { - let id = client.create_market(&creator, &default_params(&env)); - env.ledger().set_timestamp(env.ledger().timestamp() + 2000); - client.resolve_market(&oracle, &id, &symbol_short!("yes")); - } - - let stats = client.get_creator_stats(&creator); - assert!(stats.reputation_score <= 1000); - } -} +// Tests have been moved to tests/reputation_tests.rs diff --git a/contract/src/season.rs b/contract/src/season.rs index 0687922f..8488b23d 100644 --- a/contract/src/season.rs +++ b/contract/src/season.rs @@ -1,12 +1,12 @@ -use soroban_sdk::{symbol_short, Address, Env, Vec}; -use crate::leaderboard; use crate::config::{self, PERSISTENT_BUMP, PERSISTENT_THRESHOLD}; use crate::errors::InsightArenaError; use crate::escrow; +use crate::leaderboard; use crate::storage_types::{ DataKey, LeaderboardEntry, LeaderboardSnapshot, RewardPayout, Season, UserProfile, }; use crate::ttl; +use soroban_sdk::{symbol_short, Address, Env, Vec}; fn bump_season(env: &Env, season_id: u32) { ttl::extend_season_ttl(env, season_id); @@ -399,7 +399,7 @@ pub fn update_leaderboard( } let updated_at = env.ledger().timestamp(); - + // Call the persistence layer in leaderboard.rs leaderboard::store_snapshot( env, diff --git a/contract/src/season_tests.rs b/contract/src/season_tests.rs index 39066c26..e3f5961a 100644 --- a/contract/src/season_tests.rs +++ b/contract/src/season_tests.rs @@ -367,7 +367,8 @@ fn test_points_accumulate_across_markets() { ); let profile = client.get_user_stats(&winner); - let expected_points = calculate_expected_points(50_000_000, 1, 1) + calculate_expected_points(30_000_000, 2, 2); + let expected_points = + calculate_expected_points(50_000_000, 1, 1) + calculate_expected_points(30_000_000, 2, 2); assert_eq!(profile.season_points, expected_points); assert_eq!(profile.total_winnings, first_payout + second_payout); @@ -392,4 +393,4 @@ fn calculate_expected_points(stake_amount: i128, correct: u32, total: u32) -> u3 } else { res as u32 } -} \ No newline at end of file +} diff --git a/contract/src/ttl.rs b/contract/src/ttl.rs index 0c730075..f52677b3 100644 --- a/contract/src/ttl.rs +++ b/contract/src/ttl.rs @@ -84,4 +84,3 @@ pub fn extend_season_ttl(env: &Env, season_id: u32) { ); } } - diff --git a/contract/tests/escrow_tests.rs b/contract/tests/escrow_tests.rs index de1ee766..9835c9e4 100644 --- a/contract/tests/escrow_tests.rs +++ b/contract/tests/escrow_tests.rs @@ -645,9 +645,18 @@ fn test_payout_with_zero_losers() { }); let token_client = TokenClient::new(&env, &xlm_token); - assert_eq!(token_client.balance(&predictor_a), balance_a_before + stake_a); - assert_eq!(token_client.balance(&predictor_b), balance_b_before + stake_b); - assert_eq!(token_client.balance(&predictor_c), balance_c_before + stake_c); + assert_eq!( + token_client.balance(&predictor_a), + balance_a_before + stake_a + ); + assert_eq!( + token_client.balance(&predictor_b), + balance_b_before + stake_b + ); + assert_eq!( + token_client.balance(&predictor_c), + balance_c_before + stake_c + ); let final_balance = env.as_contract(&client.address, || get_contract_balance(&env)); assert_eq!(final_balance, 0); diff --git a/contract/tests/oracle_tests.rs b/contract/tests/oracle_tests.rs index baf52bfd..90214dbd 100644 --- a/contract/tests/oracle_tests.rs +++ b/contract/tests/oracle_tests.rs @@ -2,11 +2,7 @@ use soroban_sdk::testutils::{Address as _, Ledger as _}; use soroban_sdk::{symbol_short, vec, Address, Env, String, Symbol}; use insightarena_contract::market::CreateMarketParams; -use insightarena_contract::{ - InsightArenaContract, - InsightArenaContractClient, - InsightArenaError, -}; +use insightarena_contract::{InsightArenaContract, InsightArenaContractClient, InsightArenaError}; fn register_token(env: &Env) -> Address { let token_admin = Address::generate(env); @@ -181,5 +177,8 @@ fn test_resolve_market_before_resolution_time() { // Do NOT advance time; attempt to resolve immediately let result = client.try_resolve_market(&oracle, &id, &symbol_short!("yes")); - assert!(matches!(result, Err(Ok(InsightArenaError::MarketStillOpen)))); + assert!(matches!( + result, + Err(Ok(InsightArenaError::MarketStillOpen)) + )); } diff --git a/contract/tests/reputation_tests.rs b/contract/tests/reputation_tests.rs new file mode 100644 index 00000000..f221d076 --- /dev/null +++ b/contract/tests/reputation_tests.rs @@ -0,0 +1,260 @@ +#![cfg(test)] + +use insightarena_contract::market::CreateMarketParams; +use insightarena_contract::reputation::*; +use insightarena_contract::storage_types::CreatorStats; +use insightarena_contract::{InsightArenaContract, InsightArenaContractClient}; +use soroban_sdk::testutils::{Address as _, Ledger as _}; +use soroban_sdk::{symbol_short, vec, Address, Env, String, Symbol}; + +fn register_token(env: &Env) -> Address { + let token_admin = Address::generate(env); + env.register_stellar_asset_contract_v2(token_admin) + .address() +} + +fn deploy(env: &Env) -> (InsightArenaContractClient<'_>, Address, Address) { + let id = env.register(InsightArenaContract, ()); + let client = InsightArenaContractClient::new(env, &id); + let admin = Address::generate(env); + let oracle = Address::generate(env); + let xlm_token = register_token(env); + env.mock_all_auths(); + client.initialize(&admin, &oracle, &200_u32, &xlm_token); + (client, admin, oracle) +} + +fn default_params(env: &Env) -> CreateMarketParams { + let now = env.ledger().timestamp(); + CreateMarketParams { + title: String::from_str(env, "Test market"), + description: String::from_str(env, "desc"), + category: Symbol::new(env, "Sports"), + outcomes: vec![env, symbol_short!("yes"), symbol_short!("no")], + end_time: now + 1000, + resolution_time: now + 2000, + dispute_window: 86_400, + creator_fee_bps: 100, + min_stake: 10_000_000, + max_stake: 100_000_000, + is_public: true, + } +} + +// ── Pure formula tests ──────────────────────────────────────────────────── + +#[test] +fn reputation_zero_for_new_creator() { + let stats = CreatorStats { + markets_created: 0, + markets_resolved: 0, + average_participant_count: 0, + dispute_count: 0, + reputation_score: 0, + }; + assert_eq!(calculate_creator_reputation(&stats), 0); +} + +#[test] +fn reputation_perfect_score_no_disputes() { + // 10/10 resolved, 100 avg participants → 600 + 200 - 0 = 800 + let stats = CreatorStats { + markets_created: 10, + markets_resolved: 10, + average_participant_count: 100, + dispute_count: 0, + reputation_score: 0, + }; + assert_eq!(calculate_creator_reputation(&stats), 800); +} + +#[test] +fn reputation_clamped_to_1000() { + let stats = CreatorStats { + markets_created: 1, + markets_resolved: 1, + average_participant_count: 300, // bonus capped at 200 + dispute_count: 0, + reputation_score: 0, + }; + // 600 + 200 = 800 + assert_eq!(calculate_creator_reputation(&stats), 800); +} + +#[test] +fn reputation_dispute_penalty_capped_at_200() { + // 10 * 50 = 500, capped at 200 → 600 + 0 - 200 = 400 + let stats = CreatorStats { + markets_created: 10, + markets_resolved: 10, + average_participant_count: 0, + dispute_count: 10, + reputation_score: 0, + }; + assert_eq!(calculate_creator_reputation(&stats), 400); +} + +#[test] +fn reputation_never_underflows() { + // 0 resolved, max disputes → saturating_sub → 0 + let stats = CreatorStats { + markets_created: 10, + markets_resolved: 0, + average_participant_count: 0, + dispute_count: 100, + reputation_score: 0, + }; + assert_eq!(calculate_creator_reputation(&stats), 0); +} + +#[test] +fn reputation_partial_resolution() { + // 5/10 * 600 = 300, 10 * 2 = 20, 1 * 50 = 50 → 270 + let stats = CreatorStats { + markets_created: 10, + markets_resolved: 5, + average_participant_count: 10, + dispute_count: 1, + reputation_score: 0, + }; + assert_eq!(calculate_creator_reputation(&stats), 270); +} + +#[test] +fn reputation_participation_bonus_capped_at_200() { + let stats = CreatorStats { + markets_created: 1, + markets_resolved: 1, + average_participant_count: 200, // 200 * 2 = 400, capped at 200 + dispute_count: 0, + reputation_score: 0, + }; + assert_eq!(calculate_creator_reputation(&stats), 800); +} + +// ── Integration tests ───────────────────────────────────────────────────── + +#[test] +fn get_creator_stats_returns_default_for_unknown_creator() { + let env = Env::default(); + env.mock_all_auths(); + let (client, _, _) = deploy(&env); + let unknown = Address::generate(&env); + + let stats = client.get_creator_stats(&unknown); + assert_eq!(stats.markets_created, 0); + assert_eq!(stats.markets_resolved, 0); + assert_eq!(stats.reputation_score, 0); +} + +#[test] +fn stats_updated_on_market_creation() { + let env = Env::default(); + env.mock_all_auths(); + let (client, _, _) = deploy(&env); + let creator = Address::generate(&env); + + client.create_market(&creator, &default_params(&env)); + + let stats = client.get_creator_stats(&creator); + assert_eq!(stats.markets_created, 1); + assert_eq!(stats.markets_resolved, 0); +} + +#[test] +fn stats_updated_on_market_resolution() { + let env = Env::default(); + env.mock_all_auths(); + let (client, _, oracle) = deploy(&env); + let creator = Address::generate(&env); + + let id = client.create_market(&creator, &default_params(&env)); + env.ledger().set_timestamp(env.ledger().timestamp() + 2000); + client.resolve_market(&oracle, &id, &symbol_short!("yes")); + + let stats = client.get_creator_stats(&creator); + assert_eq!(stats.markets_created, 1); + assert_eq!(stats.markets_resolved, 1); +} + +#[test] +fn stats_accumulate_across_multiple_markets() { + let env = Env::default(); + env.mock_all_auths(); + let (client, _, oracle) = deploy(&env); + let creator = Address::generate(&env); + + let id1 = client.create_market(&creator, &default_params(&env)); + let id2 = client.create_market(&creator, &default_params(&env)); + + let stats = client.get_creator_stats(&creator); + assert_eq!(stats.markets_created, 2); + + env.ledger().set_timestamp(env.ledger().timestamp() + 2000); + client.resolve_market(&oracle, &id1, &symbol_short!("yes")); + client.resolve_market(&oracle, &id2, &symbol_short!("no")); + + let stats = client.get_creator_stats(&creator); + assert_eq!(stats.markets_resolved, 2); +} + +#[test] +fn reputation_score_stored_in_stats() { + let env = Env::default(); + env.mock_all_auths(); + let (client, _, oracle) = deploy(&env); + let creator = Address::generate(&env); + + let id = client.create_market(&creator, &default_params(&env)); + env.ledger().set_timestamp(env.ledger().timestamp() + 2000); + client.resolve_market(&oracle, &id, &symbol_short!("yes")); + + let stats = client.get_creator_stats(&creator); + // 1/1 resolved = 600, 0 participants, 0 disputes → 600 + assert_eq!(stats.reputation_score, 600); +} + +#[test] +fn reputation_score_always_in_range() { + let env = Env::default(); + env.mock_all_auths(); + let (client, _, oracle) = deploy(&env); + let creator = Address::generate(&env); + + for _ in 0..3 { + let id = client.create_market(&creator, &default_params(&env)); + env.ledger().set_timestamp(env.ledger().timestamp() + 2000); + client.resolve_market(&oracle, &id, &symbol_short!("yes")); + } + + let stats = client.get_creator_stats(&creator); + assert!(stats.reputation_score <= 1000); +} + +#[test] +fn test_reputation_decay_over_time() { + // Test that reputation scores decay appropriately over time + // Ensures inactive users don't maintain high scores indefinitely + let env = Env::default(); + env.mock_all_auths(); + + let (client, _, oracle) = deploy(&env); + let creator = Address::generate(&env); + + // Create and resolve market to get positive reputation + let id = client.create_market(&creator, &default_params(&env)); + env.ledger().set_timestamp(env.ledger().timestamp() + 2000); + client.resolve_market(&oracle, &id, &symbol_short!("yes")); + + let stats = client.get_creator_stats(&creator); + assert_eq!(stats.reputation_score, 600); + + // Fast forward in time + env.ledger() + .set_timestamp(env.ledger().timestamp() + 86400 * 30); // 30 days + let stats_after_time = client.get_creator_stats(&creator); + + // Update this when decay logic is implemented in the reputation formula + // For now we assert the current behavior where stats aren't decayed + assert_eq!(stats_after_time.reputation_score, 600); +}