From deb3c39b089222d4a2d2a9345762782f5f8c1d9f Mon Sep 17 00:00:00 2001 From: Lex Studios Date: Mon, 30 Mar 2026 05:19:42 +0000 Subject: [PATCH 1/6] feat(#373-#376): Implement calculate_liquidity_value function and comprehensive tests - Implement calculate_liquidity_value for LP token redemption (#373) - Add 4 basic liquidity value calculation tests (#373) - Add 4 edge case tests for multiple LPs and large pools (#374) - Add 4 validation tests for error handling (#375) - Add 1 precision test for rounding accuracy (#376) Total: 13 new tests covering all scenarios including: - Full and partial withdrawals - Fee accumulation - Multiple LP positions - Small and large pool operations - Zero/negative token validation - Supply overflow protection - Precision verification --- contract/src/liquidity.rs | 140 +++++++++++++++++++++++++++++++++++++- 1 file changed, 139 insertions(+), 1 deletion(-) diff --git a/contract/src/liquidity.rs b/contract/src/liquidity.rs index c115e1a9..f6397390 100644 --- a/contract/src/liquidity.rs +++ b/contract/src/liquidity.rs @@ -51,7 +51,28 @@ pub fn calculate_swap_output( // ── Helper Functions ────────────────────────────────────────────────────────── -// TODO: Add helper functions +/// Calculate liquidity value for LP tokens (for withdrawal) +pub fn calculate_liquidity_value( + lp_tokens: i128, + total_liquidity: i128, + total_lp_supply: i128, +) -> Result { + if lp_tokens <= 0 || total_lp_supply <= 0 { + return Err(InsightArenaError::InvalidInput); + } + + if lp_tokens > total_lp_supply { + return Err(InsightArenaError::InsufficientBalance); + } + + let value = lp_tokens + .checked_mul(total_liquidity) + .ok_or(InsightArenaError::Overflow)? + .checked_div(total_lp_supply) + .ok_or(InsightArenaError::Overflow)?; + + Ok(value) +} // ── Liquidity Management ────────────────────────────────────────────────────── @@ -147,4 +168,121 @@ mod tests { // Deposit: 2000, Liquidity: 1000, Supply: 1000 → Expected: 2000 assert_eq!(calculate_lp_tokens(2000, 1000, 1000), Ok(2000)); } + + // ── Issue #373: Basic Liquidity Value Calculation Tests ────────────────── + + #[test] + fn test_calculate_liquidity_value_full_withdrawal() { + // LP: 1000, Liquidity: 1000, Supply: 1000 → Expected: 1000 + assert_eq!( + calculate_liquidity_value(1000, 1000, 1000), + Ok(1000) + ); + } + + #[test] + fn test_calculate_liquidity_value_half_withdrawal() { + // LP: 500, Liquidity: 1000, Supply: 1000 → Expected: 500 + assert_eq!( + calculate_liquidity_value(500, 1000, 1000), + Ok(500) + ); + } + + #[test] + fn test_calculate_liquidity_value_quarter_withdrawal() { + // LP: 250, Liquidity: 1000, Supply: 1000 → Expected: 250 + assert_eq!( + calculate_liquidity_value(250, 1000, 1000), + Ok(250) + ); + } + + #[test] + fn test_calculate_liquidity_value_with_fees() { + // LP: 1000, Liquidity: 1100, Supply: 1000 → Expected: 1100 + assert_eq!( + calculate_liquidity_value(1000, 1100, 1000), + Ok(1100) + ); + } + + // ── Issue #374: Edge Case Tests ────────────────────────────────────────── + + #[test] + fn test_calculate_liquidity_value_multiple_lps() { + // LP: 300, Liquidity: 1500, Supply: 1000 → Expected: 450 + assert_eq!( + calculate_liquidity_value(300, 1500, 1000), + Ok(450) + ); + } + + #[test] + fn test_calculate_liquidity_value_small_withdrawal() { + // LP: 1, Liquidity: 1_000_000, Supply: 1_000_000 → Expected: 1 + assert_eq!( + calculate_liquidity_value(1, 1_000_000, 1_000_000), + Ok(1) + ); + } + + #[test] + fn test_calculate_liquidity_value_large_pool() { + // LP: 100, Liquidity: 10_000_000, Supply: 1_000_000 → Expected: 1000 + assert_eq!( + calculate_liquidity_value(100, 10_000_000, 1_000_000), + Ok(1000) + ); + } + + #[test] + fn test_calculate_liquidity_value_after_trading() { + // LP: 500, Liquidity: 1050, Supply: 1000 → Expected: 525 + assert_eq!( + calculate_liquidity_value(500, 1050, 1000), + Ok(525) + ); + } + + // ── Issue #375: Validation Tests ───────────────────────────────────────── + + #[test] + fn test_calculate_liquidity_value_zero_tokens_fails() { + // Should return InvalidInput error + let result = calculate_liquidity_value(0, 1000, 1000); + assert_eq!(result, Err(InsightArenaError::InvalidInput)); + } + + #[test] + fn test_calculate_liquidity_value_negative_tokens_fails() { + // Should return InvalidInput error + let result = calculate_liquidity_value(-1, 1000, 1000); + assert_eq!(result, Err(InsightArenaError::InvalidInput)); + } + + #[test] + fn test_calculate_liquidity_value_exceeds_supply_fails() { + // LP: 1500, Supply: 1000 → Should return InsufficientBalance error + let result = calculate_liquidity_value(1500, 1000, 1000); + assert_eq!(result, Err(InsightArenaError::InsufficientBalance)); + } + + #[test] + fn test_calculate_liquidity_value_overflow_protection() { + // Try: i128::MAX as lp_tokens → Should return Overflow error + let result = calculate_liquidity_value(i128::MAX, i128::MAX, 1000); + assert_eq!(result, Err(InsightArenaError::Overflow)); + } + + // ── Issue #376: Precision Test ─────────────────────────────────────────── + + #[test] + fn test_calculate_liquidity_value_precision() { + // LP: 333, Liquidity: 1000, Supply: 1000 → Expected: 333 (no rounding errors) + assert_eq!( + calculate_liquidity_value(333, 1000, 1000), + Ok(333) + ); + } } From 25937130e89747762847e10857a162606f5a14c3 Mon Sep 17 00:00:00 2001 From: Lex Studios Date: Mon, 30 Mar 2026 07:56:02 +0000 Subject: [PATCH 2/6] fix: suppress dead_code warning for calculate_liquidity_value function --- contract/src/liquidity.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/contract/src/liquidity.rs b/contract/src/liquidity.rs index 4ae00f61..df5158b3 100644 --- a/contract/src/liquidity.rs +++ b/contract/src/liquidity.rs @@ -52,6 +52,7 @@ pub fn calculate_swap_output( // ── Helper Functions ────────────────────────────────────────────────────────── /// Calculate liquidity value for LP tokens (for withdrawal) +#[allow(dead_code)] pub fn calculate_liquidity_value( lp_tokens: i128, total_liquidity: i128, From 330014b2ce61d02beb5debd5e9923ae1601153f9 Mon Sep 17 00:00:00 2001 From: Lex Studios Date: Mon, 30 Mar 2026 08:07:04 +0000 Subject: [PATCH 3/6] fix: export liquidity functions from lib.rs for test access --- contract/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/contract/src/lib.rs b/contract/src/lib.rs index 0475d8ea..85a7f0a5 100644 --- a/contract/src/lib.rs +++ b/contract/src/lib.rs @@ -23,6 +23,7 @@ pub mod ttl; pub use crate::config::Config; pub use crate::errors::InsightArenaError; pub use crate::governance::{Proposal, ProposalType}; +pub use crate::liquidity::{calculate_lp_tokens, calculate_liquidity_value, calculate_swap_output}; pub use crate::market::CreateMarketParams; pub use crate::storage_types::{ CreatorStats, DataKey, InviteCode, LeaderboardEntry, LeaderboardSnapshot, Market, MarketStats, From 4cfa968e0c1d68ec5006f2b67dfdc77e2cbe7e11 Mon Sep 17 00:00:00 2001 From: Lex Studios Date: Mon, 30 Mar 2026 08:12:23 +0000 Subject: [PATCH 4/6] fix: correct error type and remove unused import --- contract/src/leaderboard.rs | 2 +- contract/src/liquidity.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/contract/src/leaderboard.rs b/contract/src/leaderboard.rs index f77b75a1..d42cf1ee 100644 --- a/contract/src/leaderboard.rs +++ b/contract/src/leaderboard.rs @@ -1,6 +1,6 @@ use crate::errors::InsightArenaError; use crate::storage_types::{DataKey, LeaderboardSnapshot, Season, UserProfile}; -use soroban_sdk::{Address, Env, Vec}; +use soroban_sdk::{Address, Env}; /// `stake_bonus = floor(stake_xlm / 10)` → `floor(stake_stroops / 10^8 stroops)`. const STROOPS_PER_STAKE_POINT: i128 = 100_000_000; diff --git a/contract/src/liquidity.rs b/contract/src/liquidity.rs index df5158b3..d1184fa1 100644 --- a/contract/src/liquidity.rs +++ b/contract/src/liquidity.rs @@ -63,7 +63,7 @@ pub fn calculate_liquidity_value( } if lp_tokens > total_lp_supply { - return Err(InsightArenaError::InsufficientBalance); + return Err(InsightArenaError::InsufficientFunds); } let value = lp_tokens From b196d382c7c1953542321be9dfd33fcb56d9e22c Mon Sep 17 00:00:00 2001 From: Lex Studios Date: Mon, 30 Mar 2026 08:13:26 +0000 Subject: [PATCH 5/6] fix: re-export liquidity module for test access --- contract/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/contract/src/lib.rs b/contract/src/lib.rs index 85a7f0a5..aaf5f837 100644 --- a/contract/src/lib.rs +++ b/contract/src/lib.rs @@ -23,6 +23,7 @@ pub mod ttl; pub use crate::config::Config; pub use crate::errors::InsightArenaError; pub use crate::governance::{Proposal, ProposalType}; +pub use crate::liquidity; pub use crate::liquidity::{calculate_lp_tokens, calculate_liquidity_value, calculate_swap_output}; pub use crate::market::CreateMarketParams; pub use crate::storage_types::{ From 387c305e4e1d2cb0ca6c0ce24129f04a86867f4c Mon Sep 17 00:00:00 2001 From: Lex Studios Date: Mon, 30 Mar 2026 08:19:39 +0000 Subject: [PATCH 6/6] fix: remove duplicate liquidity module export --- contract/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/contract/src/lib.rs b/contract/src/lib.rs index aaf5f837..85a7f0a5 100644 --- a/contract/src/lib.rs +++ b/contract/src/lib.rs @@ -23,7 +23,6 @@ pub mod ttl; pub use crate::config::Config; pub use crate::errors::InsightArenaError; pub use crate::governance::{Proposal, ProposalType}; -pub use crate::liquidity; pub use crate::liquidity::{calculate_lp_tokens, calculate_liquidity_value, calculate_swap_output}; pub use crate::market::CreateMarketParams; pub use crate::storage_types::{