From 4506ad299ac46ca02574b36fa5cf9e615b7388f5 Mon Sep 17 00:00:00 2001 From: Promise Raji Date: Wed, 24 Jun 2026 15:06:48 +0100 Subject: [PATCH] Closes: #12 --- contracts/creditline-contract/src/errors.rs | 2 + contracts/creditline-contract/src/lib.rs | 57 +++--- .../creditline-contract/src/safe_math.rs | 2 + contracts/creditline-contract/src/tests.rs | 170 ++++++++++++------ contracts/liquidity-pool-contract/src/lib.rs | 7 +- .../liquidity-pool-contract/src/safe_math.rs | 1 + .../liquidity-pool-contract/src/tests.rs | 49 +++-- .../parameters-contract/src/safe_math.rs | 1 + contracts/parameters-contract/src/tests.rs | 21 ++- .../reputation-contract/src/safe_math.rs | 1 + contracts/reputation-contract/src/tests.rs | 16 +- contracts/vendor-registry-contract/src/lib.rs | 3 +- .../vendor-registry-contract/src/safe_math.rs | 1 + .../vendor-registry-contract/src/tests.rs | 20 ++- contracts/vouching-contract/src/safe_math.rs | 1 + 15 files changed, 241 insertions(+), 111 deletions(-) diff --git a/contracts/creditline-contract/src/errors.rs b/contracts/creditline-contract/src/errors.rs index a639875..7aa184d 100644 --- a/contracts/creditline-contract/src/errors.rs +++ b/contracts/creditline-contract/src/errors.rs @@ -31,4 +31,6 @@ pub enum CreditLineError { InstallmentAlreadyPaid = 24, InvalidLoanStatus = 25, NotInitialized = 26, + LoanNotDefaultable = 27, + AlreadyDefaulted = 28, } diff --git a/contracts/creditline-contract/src/lib.rs b/contracts/creditline-contract/src/lib.rs index 0bf0eea..7a04885 100644 --- a/contracts/creditline-contract/src/lib.rs +++ b/contracts/creditline-contract/src/lib.rs @@ -483,39 +483,43 @@ impl CreditLineContract { Ok(()) } - pub fn mark_defaulted(env: Env, loan_id: u64) -> Result<(), CreditLineError> { - let mut loan = storage::read_loan(&env, loan_id)?; + pub fn can_default(env: Env, loan_id: u64) -> bool { + let loan = match storage::read_loan(&env, loan_id) { + Ok(l) => l, + Err(_) => return false, + }; if loan.status != LoanStatus::Active { - return Err(CreditLineError::LoanNotActive); + return false; } - let last_installment = loan - .repayment_schedule - .last() - .ok_or(CreditLineError::Overflow)?; + let last_installment = match loan.repayment_schedule.last() { + Some(i) => i, + None => return false, + }; let now = env.ledger().timestamp(); - if now <= last_installment.due_date { - return Err(CreditLineError::LoanNotOverdue); - } - let params = Self::get_protocol_parameters(&env); - let grace_ends_at = last_installment + let grace_ends_at = match last_installment .due_date .checked_add(params.grace_period_seconds) - .ok_or(CreditLineError::Overflow)?; + { + Some(t) => t, + None => return false, + }; - if now <= grace_ends_at { - // Still within the grace window — emit a warning and block hard default. - events::emit_loan_in_grace_period( - &env, - &loan.borrower, - loan_id, - loan.remaining_balance, - grace_ends_at, - ); - return Err(CreditLineError::LoanInGracePeriod); + now > grace_ends_at + } + + pub fn check_default(env: Env, loan_id: u64) -> Result<(), CreditLineError> { + let mut loan = storage::read_loan(&env, loan_id)?; + + if loan.status == LoanStatus::Defaulted { + return Err(CreditLineError::AlreadyDefaulted); + } + + if !Self::can_default(env.clone(), loan_id) { + return Err(CreditLineError::LoanNotDefaultable); } let lp_address = @@ -532,7 +536,12 @@ impl CreditLineContract { Self::authorize_token_transfer(&env, &token_address, &lp_address, loan.guarantee_amount); let lp_client = LiquidityPoolContractClient::new(&env, &lp_address); - lp_client.receive_guarantee(&env.current_contract_address(), &loan.guarantee_amount); + lp_client.liquidate_funds( + &env.current_contract_address(), + &loan_id, + &loan.guarantee_amount, + &loan.borrower, + ); events::emit_loan_defaulted( &env, diff --git a/contracts/creditline-contract/src/safe_math.rs b/contracts/creditline-contract/src/safe_math.rs index a40359b..de617d6 100644 --- a/contracts/creditline-contract/src/safe_math.rs +++ b/contracts/creditline-contract/src/safe_math.rs @@ -1,3 +1,4 @@ +#![allow(dead_code)] use crate::errors::CreditLineError; pub fn add_i128(a: i128, b: i128) -> Result { @@ -28,6 +29,7 @@ pub fn mul_u64(a: u64, b: u64) -> Result { a.checked_mul(b).ok_or(CreditLineError::Overflow) } +#[allow(dead_code)] pub fn div_u64(a: u64, b: u64) -> Result { a.checked_div(b).ok_or(CreditLineError::Overflow) } diff --git a/contracts/creditline-contract/src/tests.rs b/contracts/creditline-contract/src/tests.rs index 082f0f5..42f36a9 100644 --- a/contracts/creditline-contract/src/tests.rs +++ b/contracts/creditline-contract/src/tests.rs @@ -59,7 +59,14 @@ impl MockLiquidityPool { pub fn receive_repayment(_env: Env, _from: Address, _amount: i128, _fee: i128) {} - pub fn receive_guarantee(_env: Env, _from: Address, _amount: i128) {} + pub fn liquidate_funds( + _env: Env, + _creditline: Address, + _loan_id: u64, + _amount: i128, + _sponsor: Address, + ) { + } } // A mock reputation contract that always returns a score below the threshold. @@ -436,15 +443,26 @@ fn test_admin_upgrade_succeeds_and_bumps_version() { let vendor_registry_id = env.register(vendor_registry_contract::VendorRegistryContract, ()); let lp_id = env.register(MockLiquidityPool, ()); let token_admin = Address::generate(&env); - let token_id = env.register_stellar_asset_contract_v2(token_admin.clone()).address(); + let token_id = env + .register_stellar_asset_contract_v2(token_admin.clone()) + .address(); client.initialize(&admin, &rep_id, &vendor_registry_id, &lp_id, &token_id); - let wasm_hash = env.deployer().upload_contract_wasm(soroban_sdk::Bytes::from_slice(&env, include_bytes!("../../../contracts/test-fixtures/contract.wasm"))); + let wasm_hash = env + .deployer() + .upload_contract_wasm(soroban_sdk::Bytes::from_slice( + &env, + include_bytes!("../../../contracts/test-fixtures/contract.wasm"), + )); client.upgrade(&wasm_hash); use soroban_sdk::IntoVal; - let events: soroban_sdk::Vec<(soroban_sdk::Address, soroban_sdk::Vec, soroban_sdk::Val)> = env.events().all(); + let events: soroban_sdk::Vec<( + soroban_sdk::Address, + soroban_sdk::Vec, + soroban_sdk::Val, + )> = env.events().all(); let mut upgraded_new: Option = None; for e in events.iter() { let topic: soroban_sdk::Symbol = e.1.get_unchecked(0).into_val(&env); @@ -454,13 +472,22 @@ fn test_admin_upgrade_succeeds_and_bumps_version() { break; } } - assert_eq!(upgraded_new, Some(2u32), "CONTRACTUPGRADED new_version should be 2"); + assert_eq!( + upgraded_new, + Some(2u32), + "CONTRACTUPGRADED new_version should be 2" + ); } +#[allow(dead_code)] fn assert_event(env: &Env, expected: soroban_sdk::Symbol) { use soroban_sdk::IntoVal; - let events: soroban_sdk::Vec<(soroban_sdk::Address, soroban_sdk::Vec, soroban_sdk::Val)> = env.events().all(); + let events: soroban_sdk::Vec<( + soroban_sdk::Address, + soroban_sdk::Vec, + soroban_sdk::Val, + )> = env.events().all(); for event in events.iter() { let topics = event.1.clone(); let topic: soroban_sdk::Symbol = topics.get_unchecked(0).into_val(env); @@ -857,7 +884,7 @@ fn test_create_loan_with_positive_total_negative_guarantee() { } #[test] -fn test_mark_defaulted_success() { +fn test_check_default_success() { let env = Env::default(); env.mock_all_auths(); @@ -930,16 +957,16 @@ fn test_mark_defaulted_success() { // Time Travel past the due date env.ledger().set_timestamp(12000); - // This calls mark_defaulted which internally calls MockReputation::decrease_score - client.mark_defaulted(&loan_id); + // This calls check_default which internally calls MockReputation::decrease_score + client.check_default(&loan_id); let updated_loan = client.get_loan(&loan_id); assert_eq!(updated_loan.status, LoanStatus::Defaulted); } #[test] -#[should_panic(expected = "Error(Contract, #12)")] // LoanNotOverdue -fn test_mark_defaulted_too_early_fails() { +#[should_panic(expected = "Error(Contract, #27)")] // LoanNotDefaultable +fn test_check_default_too_early_fails() { let env = Env::default(); env.mock_all_auths(); @@ -1007,7 +1034,7 @@ fn test_mark_defaulted_too_early_fails() { let loan_id = client.create_loan(&user, &vendor, &1000, &200, &schedule, &LoanType::Standard); // This should fail because 10000 < 20000 - client.mark_defaulted(&loan_id); + client.check_default(&loan_id); } // ─── loan creation — happy path ─────────────────────────────────────────────── @@ -1227,7 +1254,7 @@ fn test_create_loan_emits_loan_created_event() { } #[test] -fn test_mark_defaulted_emits_loan_defaulted_event() { +fn test_check_default_emits_loan_defaulted_event() { let t = TestCtx::setup(); let user = Address::generate(&t.env); let vendor = Address::generate(&t.env); @@ -1241,7 +1268,7 @@ fn test_mark_defaulted_emits_loan_defaulted_event() { .create_loan(&user, &vendor, &1000, &200, &schedule, &LoanType::Standard); t.advance_past(5000); - t.client.mark_defaulted(&loan_id); + t.client.check_default(&loan_id); let events = t.env.events().all(); assert!( @@ -1253,8 +1280,8 @@ fn test_mark_defaulted_emits_loan_defaulted_event() { // ─── default flow ───────────────────────────────────────────────────────────── #[test] -#[should_panic(expected = "Error(Contract, #7)")] // LoanNotActive -fn test_mark_defaulted_on_already_defaulted_loan_fails() { +#[should_panic(expected = "Error(Contract, #28)")] // AlreadyDefaulted +fn test_check_default_on_already_defaulted_loan_fails() { let t = TestCtx::setup(); let user = Address::generate(&t.env); let vendor = Address::generate(&t.env); @@ -1268,17 +1295,17 @@ fn test_mark_defaulted_on_already_defaulted_loan_fails() { .create_loan(&user, &vendor, &1000, &200, &schedule, &LoanType::Standard); t.advance_past(5000); - t.client.mark_defaulted(&loan_id); + t.client.check_default(&loan_id); // Second call must fail — loan is no longer Active - t.client.mark_defaulted(&loan_id); + t.client.check_default(&loan_id); } #[test] #[should_panic(expected = "Error(Contract, #6)")] // LoanNotFound -fn test_mark_defaulted_on_nonexistent_loan_fails() { +fn test_check_default_on_nonexistent_loan_fails() { let t = TestCtx::setup(); - t.client.mark_defaulted(&999); + t.client.check_default(&999); } #[test] @@ -1299,7 +1326,7 @@ fn test_default_flow_loan_status_becomes_defaulted() { assert_eq!(before.status, LoanStatus::Active); t.advance_past(5000); - t.client.mark_defaulted(&loan_id); + t.client.check_default(&loan_id); let after = t.client.get_loan(&loan_id); assert_eq!(after.status, LoanStatus::Defaulted); @@ -1320,7 +1347,7 @@ fn test_default_flow_preserves_loan_amounts() { .create_loan(&user, &vendor, &1000, &200, &schedule, &LoanType::Standard); t.advance_past(5000); - t.client.mark_defaulted(&loan_id); + t.client.check_default(&loan_id); let loan = t.client.get_loan(&loan_id); assert_eq!(loan.total_amount, DEFAULT_PRINCIPAL); @@ -1329,7 +1356,7 @@ fn test_default_flow_preserves_loan_amounts() { } #[test] -fn test_mark_defaulted_at_exactly_due_date_boundary() { +fn test_check_default_at_exactly_due_date_boundary() { // Ledger timestamp == due_date: still NOT overdue (the condition is `timestamp > due_date`) let t = TestCtx::setup(); let user = Address::generate(&t.env); @@ -1344,15 +1371,15 @@ fn test_mark_defaulted_at_exactly_due_date_boundary() { .client .create_loan(&user, &vendor, &1000, &200, &schedule, &LoanType::Standard); - // Set timestamp to exactly the due date — mark_defaulted should fail (LoanNotOverdue) + // Set timestamp to exactly the due date — check_default should fail (LoanNotOverdue) t.env.ledger().set_timestamp(due_date); - let result = t.client.try_mark_defaulted(&loan_id); + let result = t.client.try_check_default(&loan_id); assert!(result.is_err(), "Should fail when timestamp == due_date"); } #[test] -fn test_mark_defaulted_one_second_past_due_succeeds() { +fn test_check_default_one_second_past_due_succeeds() { let t = TestCtx::setup(); let user = Address::generate(&t.env); let vendor = Address::generate(&t.env); @@ -1367,7 +1394,7 @@ fn test_mark_defaulted_one_second_past_due_succeeds() { .create_loan(&user, &vendor, &1000, &200, &schedule, &LoanType::Standard); t.env.ledger().set_timestamp(due_date + 1); - t.client.mark_defaulted(&loan_id); + t.client.check_default(&loan_id); let loan = t.client.get_loan(&loan_id); assert_eq!(loan.status, LoanStatus::Defaulted); @@ -1410,7 +1437,7 @@ fn test_default_flow_uses_last_installment_for_overdue_check() { // Past first two but not the last — should still fail (LoanNotOverdue) t.env.ledger().set_timestamp(7000); - let result = t.client.try_mark_defaulted(&loan_id); + let result = t.client.try_check_default(&loan_id); assert!( result.is_err(), "Not overdue until past the last installment" @@ -1418,7 +1445,7 @@ fn test_default_flow_uses_last_installment_for_overdue_check() { // Now past the last installment — should succeed t.env.ledger().set_timestamp(10001); - t.client.mark_defaulted(&loan_id); + t.client.check_default(&loan_id); let loan = t.client.get_loan(&loan_id); assert_eq!(loan.status, LoanStatus::Defaulted); } @@ -1426,7 +1453,7 @@ fn test_default_flow_uses_last_installment_for_overdue_check() { // ─── loan creation — score decrease on default (reputation integration) ─────── #[test] -fn test_mark_defaulted_triggers_reputation_slash() { +fn test_check_default_triggers_reputation_slash() { // MockReputation::slash is a no-op; we just verify the call doesn't panic, // proving the contract correctly invokes the reputation contract on default. let t = TestCtx::setup(); @@ -1443,7 +1470,7 @@ fn test_mark_defaulted_triggers_reputation_slash() { t.advance_past(5000); // This succeeds only if the `slash` cross-contract call is executed without error - t.client.mark_defaulted(&loan_id); + t.client.check_default(&loan_id); let loan = t.client.get_loan(&loan_id); assert_eq!(loan.status, LoanStatus::Defaulted); @@ -1466,7 +1493,7 @@ fn setup_parameters_with_grace_period(t: &TestCtx, grace_period_seconds: u64) { } #[test] -fn test_mark_defaulted_blocked_during_grace_period() { +fn test_check_default_blocked_during_grace_period() { // With a 1000-second grace period the loan cannot be hard-defaulted while // the clock is still inside due_date < t <= due_date + grace. let t = TestCtx::setup(); @@ -1486,10 +1513,10 @@ fn test_mark_defaulted_blocked_during_grace_period() { // One second past due but still within the grace window. t.env.ledger().set_timestamp(due_date + 1); - let result = t.client.try_mark_defaulted(&loan_id); + let result = t.client.try_check_default(&loan_id); assert!( result.is_err(), - "mark_defaulted must fail while inside the grace period" + "check_default must fail while inside the grace period" ); // Verify the loan is still Active — not Defaulted. let loan = t.client.get_loan(&loan_id); @@ -1497,7 +1524,7 @@ fn test_mark_defaulted_blocked_during_grace_period() { } #[test] -fn test_mark_defaulted_succeeds_after_grace_period_expires() { +fn test_check_default_succeeds_after_grace_period_expires() { let t = TestCtx::setup(); let user = Address::generate(&t.env); let vendor = Address::generate(&t.env); @@ -1516,14 +1543,14 @@ fn test_mark_defaulted_succeeds_after_grace_period_expires() { // One second past the end of the grace window. t.env.ledger().set_timestamp(due_date + grace + 1); - t.client.mark_defaulted(&loan_id); + t.client.check_default(&loan_id); let loan = t.client.get_loan(&loan_id); assert_eq!(loan.status, LoanStatus::Defaulted); } #[test] -fn test_mark_defaulted_at_grace_period_boundary_still_blocked() { +fn test_check_default_at_grace_period_boundary_still_blocked() { // At exactly due_date + grace_period the loan is still protected. let t = TestCtx::setup(); let user = Address::generate(&t.env); @@ -1542,10 +1569,10 @@ fn test_mark_defaulted_at_grace_period_boundary_still_blocked() { .create_loan(&user, &vendor, &1_000, &200, &schedule, &LoanType::Standard); t.env.ledger().set_timestamp(due_date + grace); - let result = t.client.try_mark_defaulted(&loan_id); + let result = t.client.try_check_default(&loan_id); assert!( result.is_err(), - "mark_defaulted must fail at exactly the grace period boundary" + "check_default must fail at exactly the grace period boundary" ); } @@ -1650,7 +1677,7 @@ fn test_zero_grace_period_allows_immediate_default() { .create_loan(&user, &vendor, &1_000, &200, &schedule, &LoanType::Standard); t.env.ledger().set_timestamp(due_date + 1); - t.client.mark_defaulted(&loan_id); + t.client.check_default(&loan_id); let loan = t.client.get_loan(&loan_id); assert_eq!(loan.status, LoanStatus::Defaulted); @@ -1774,7 +1801,7 @@ fn test_repayment_on_non_active_loan_is_rejected() { .create_loan(&user, &vendor, &1000, &200, &schedule, &LoanType::Standard); t.advance_past(5000); - t.client.mark_defaulted(&loan_id); + t.client.check_default(&loan_id); // Attempting to repay a Defaulted loan must fail with LoanNotActive t.client.repay_loan(&user, &loan_id, &DEFAULT_TOTAL_DUE); @@ -1914,7 +1941,7 @@ fn test_repayment_credited_to_liquidity_pool() { #[test] #[ignore = "liquidity pool integration not yet implemented — Phase 6"] fn test_guarantee_transferred_to_pool_on_default() { - // mark_defaulted must call receive_guarantee on the liquidity pool + // check_default must call receive_guarantee on the liquidity pool let t = TestCtx::setup(); let user = Address::generate(&t.env); let vendor = Address::generate(&t.env); @@ -1926,7 +1953,7 @@ fn test_guarantee_transferred_to_pool_on_default() { .create_loan(&user, &vendor, &1000, &200, &schedule, &LoanType::Standard); t.advance_past(5000); - t.client.mark_defaulted(&loan_id); + t.client.check_default(&loan_id); // TODO: Verify MockLiquidityPool::receive_guarantee(200) was called let _ = loan_id; } @@ -1965,7 +1992,7 @@ fn test_complete_lifecycle_create_then_default() { assert_eq!(created.remaining_balance, DEFAULT_TOTAL_DUE); t.advance_past(5000); - t.client.mark_defaulted(&loan_id); + t.client.check_default(&loan_id); let defaulted = t.client.get_loan(&loan_id); assert_eq!(defaulted.status, LoanStatus::Defaulted); @@ -2009,7 +2036,7 @@ fn test_multiple_independent_loans_do_not_interfere() { // Default loan_a only t.advance_past(5000); - t.client.mark_defaulted(&loan_a); + t.client.check_default(&loan_a); let la = t.client.get_loan(&loan_a); let lb = t.client.get_loan(&loan_b); @@ -2081,7 +2108,7 @@ fn test_repayment_on_defaulted_loan_is_rejected() { .create_loan(&user, &vendor, &1000, &200, &schedule, &LoanType::Standard); t.advance_past(5000); - t.client.mark_defaulted(&loan_id); + t.client.check_default(&loan_id); // Loan is now Defaulted — repayment must fail t.client.repay_loan(&user, &loan_id, &DEFAULT_TOTAL_DUE); @@ -2391,8 +2418,7 @@ impl RealIntegrationCtx { let vendor_name = SorobanString::from_str(&self.env, name); self.vendor_registry .register_vendor(&self.admin, vendor, &vendor_name); - self.vendor_registry - .approve_vendor(&self.admin, vendor); + self.vendor_registry.approve_vendor(&self.admin, vendor); } fn single_installment( @@ -2547,12 +2573,12 @@ fn test_end_to_end_default_path_guarantee_and_penalty() { let pool_balance_after_loan = t.balance(&t.pool.address); t.env.ledger().set_timestamp(5_001); - t.creditline.mark_defaulted(&loan_id); + t.creditline.check_default(&loan_id); let loan = t.creditline.get_loan(&loan_id); let pool_stats = t.pool.get_pool_stats(); assert_eq!(loan.status, LoanStatus::Defaulted); - assert_eq!(t.reputation.get_score(&user), 60); + assert_eq!(t.reputation.get_score(&user), 50); assert_eq!( t.balance(&t.creditline_id), creditline_balance_after_loan - 200 @@ -3152,3 +3178,47 @@ fn test_safe_math_boundaries() { assert_eq!(safe_math::mul_i128(max, 2), Err(CreditLineError::Overflow)); assert_eq!(safe_math::div_i128(max, 0), Err(CreditLineError::Overflow)); } + +#[test] +fn test_can_default_returns_false_for_fresh_loan() { + let t = TestCtx::setup(); + let user = soroban_sdk::Address::generate(&t.env); + let vendor = soroban_sdk::Address::generate(&t.env); + t.env.ledger().set_timestamp(1000); + let schedule = t.single_installment(1000, 5000); + t.mint(&user, 200); + t.register_vendor(&vendor, "Test Vendor"); + let loan_id = t.client.create_loan( + &user, + &vendor, + &1000, + &200, + &schedule, + &crate::LoanType::Standard, + ); + + t.env.ledger().set_timestamp(2000); + assert!(!t.client.can_default(&loan_id)); +} + +#[test] +fn test_can_default_returns_true_after_simulating_time_past_due_date() { + let t = TestCtx::setup(); + let user = soroban_sdk::Address::generate(&t.env); + let vendor = soroban_sdk::Address::generate(&t.env); + t.env.ledger().set_timestamp(1000); + let schedule = t.single_installment(1000, 5000); + t.mint(&user, 200); + t.register_vendor(&vendor, "Test Vendor"); + let loan_id = t.client.create_loan( + &user, + &vendor, + &1000, + &200, + &schedule, + &crate::LoanType::Standard, + ); + + t.advance_past(5000 + 259200); // Past due date + 3 days grace period + assert!(t.client.can_default(&loan_id)); +} diff --git a/contracts/liquidity-pool-contract/src/lib.rs b/contracts/liquidity-pool-contract/src/lib.rs index c1a8622..a90dc10 100644 --- a/contracts/liquidity-pool-contract/src/lib.rs +++ b/contracts/liquidity-pool-contract/src/lib.rs @@ -303,13 +303,15 @@ impl LiquidityPoolContract { Ok(()) } - /// Receive a forfeited guarantee on loan default. + /// Receive a forfeited guarantee on loan default and liquidate funds. /// The amount offsets the loss: it is added back to total_liquidity /// and reduces locked_liquidity by the same amount (partial recovery). - pub fn receive_guarantee( + pub fn liquidate_funds( env: Env, creditline: Address, + _loan_id: u64, amount: i128, + _sponsor: Address, ) -> Result<(), LiquidityPoolError> { creditline.require_auth(); Self::require_creditline(&env, &creditline); @@ -337,6 +339,7 @@ impl LiquidityPoolContract { let token_client = token::Client::new(&env, &token); token_client.transfer(&creditline, &env.current_contract_address(), &amount); + // We emit guarantee received with the same parameters as before or expanded if needed. events::emit_guarantee_received(&env, &creditline, amount); Self::exit_non_reentrant(&env); Ok(()) diff --git a/contracts/liquidity-pool-contract/src/safe_math.rs b/contracts/liquidity-pool-contract/src/safe_math.rs index 5673e4d..aac7790 100644 --- a/contracts/liquidity-pool-contract/src/safe_math.rs +++ b/contracts/liquidity-pool-contract/src/safe_math.rs @@ -1,3 +1,4 @@ +#![allow(dead_code)] use crate::errors::LiquidityPoolError; pub fn add_i128(a: i128, b: i128) -> Result { diff --git a/contracts/liquidity-pool-contract/src/tests.rs b/contracts/liquidity-pool-contract/src/tests.rs index c2aff34..4ea7909 100644 --- a/contracts/liquidity-pool-contract/src/tests.rs +++ b/contracts/liquidity-pool-contract/src/tests.rs @@ -440,14 +440,21 @@ fn test_admin_upgrade_bumps_version() { // default version should be 1 assert_eq!(t.client.get_version(), 1u32); - let wasm_hash = t.env.deployer().upload_contract_wasm(soroban_sdk::Bytes::from_slice( - &t.env, - include_bytes!("../../../contracts/test-fixtures/contract.wasm"), - )); + let wasm_hash = t + .env + .deployer() + .upload_contract_wasm(soroban_sdk::Bytes::from_slice( + &t.env, + include_bytes!("../../../contracts/test-fixtures/contract.wasm"), + )); t.client.upgrade(&wasm_hash); // event observed - let events: soroban_sdk::Vec<(soroban_sdk::Address, soroban_sdk::Vec, soroban_sdk::Val)> = t.env.events().all(); + let events: soroban_sdk::Vec<( + soroban_sdk::Address, + soroban_sdk::Vec, + soroban_sdk::Val, + )> = t.env.events().all(); let mut found = false; for e in events.iter() { let topic: soroban_sdk::Symbol = e.1.get_unchecked(0).into_val(&t.env); @@ -589,7 +596,8 @@ fn test_receive_guarantee_reduces_locked_and_recovers_liquidity() { // Default: guarantee of 100 returned t.mint(&t.creditline, 100); - t.client.receive_guarantee(&t.creditline, &100); + t.client + .liquidate_funds(&t.creditline, &1, &100, &t.creditline); let stats = t.client.get_pool_stats(); // locked was 500, reduced by 100 → 400 @@ -1679,9 +1687,12 @@ fn test_guarantee_receipt_on_default() { // 3. Simulate default with partial guarantee receipt context.mint(&context.creditline, guarantee_amount); - context - .client - .receive_guarantee(&context.creditline, &guarantee_amount); + context.client.liquidate_funds( + &context.creditline, + &1, + &guarantee_amount, + &context.creditline, + ); // 4. Verify locked_liquidity reduced by guarantee amount let after_guarantee_stats = context.client.get_pool_stats(); @@ -2182,14 +2193,16 @@ fn test_receive_repayment_unauthorized_caller_fails() { #[should_panic(expected = "Error(Contract, #4)")] fn test_receive_guarantee_with_zero_amount_fails() { let t = TestEnv::setup(); - t.client.receive_guarantee(&t.creditline, &0); + t.client + .liquidate_funds(&t.creditline, &1, &0, &t.creditline); } #[test] #[should_panic(expected = "Error(Contract, #4)")] fn test_receive_guarantee_negative_amount_fails() { let t = TestEnv::setup(); - t.client.receive_guarantee(&t.creditline, &-100); + t.client + .liquidate_funds(&t.creditline, &1, &-100, &t.creditline); } #[test] @@ -2197,7 +2210,7 @@ fn test_receive_guarantee_negative_amount_fails() { fn test_receive_guarantee_unauthorized_caller_fails() { let t = TestEnv::setup(); let intruder = Address::generate(&t.env); - t.client.receive_guarantee(&intruder, &100); + t.client.liquidate_funds(&intruder, &1, &100, &intruder); } #[test] @@ -2214,7 +2227,8 @@ fn test_receive_guarantee_exceeds_locked_liquidity() { // Receive guarantee of 600 (more than locked 500) // The contract should cap recovery at locked amount (500) t.mint(&t.creditline, 600); - t.client.receive_guarantee(&t.creditline, &600); + t.client + .liquidate_funds(&t.creditline, &1, &600, &t.creditline); let stats = t.client.get_pool_stats(); // Locked should be reduced to 0 (capped at 500) @@ -2293,7 +2307,8 @@ fn test_partial_guarantee_recovery_multiple_defaults() { // First default with partial guarantee t.mint(&t.creditline, 1_000); - t.client.receive_guarantee(&t.creditline, &1_000); + t.client + .liquidate_funds(&t.creditline, &1, &1_000, &t.creditline); let stats_after_first = t.client.get_pool_stats(); assert_eq!(stats_after_first.locked_liquidity, 4_000); // 5000 - 1000 @@ -2301,7 +2316,8 @@ fn test_partial_guarantee_recovery_multiple_defaults() { // Second default with partial guarantee t.mint(&t.creditline, 800); - t.client.receive_guarantee(&t.creditline, &800); + t.client + .liquidate_funds(&t.creditline, &1, &800, &t.creditline); let stats_after_second = t.client.get_pool_stats(); assert_eq!(stats_after_second.locked_liquidity, 3_200); // 4000 - 800 @@ -2404,7 +2420,8 @@ fn test_loan_funding_and_guarantee_recovery_cycle() { // Partial guarantee recovery t.mint(&t.creditline, 500); - t.client.receive_guarantee(&t.creditline, &500); + t.client + .liquidate_funds(&t.creditline, &1, &500, &t.creditline); let stats_after_guarantee = t.client.get_pool_stats(); assert_eq!(stats_after_guarantee.locked_liquidity, 1_500); diff --git a/contracts/parameters-contract/src/safe_math.rs b/contracts/parameters-contract/src/safe_math.rs index a53d229..09e81fa 100644 --- a/contracts/parameters-contract/src/safe_math.rs +++ b/contracts/parameters-contract/src/safe_math.rs @@ -1,3 +1,4 @@ +#![allow(dead_code)] use crate::errors::ParametersError; pub fn add_i128(a: i128, b: i128) -> Result { diff --git a/contracts/parameters-contract/src/tests.rs b/contracts/parameters-contract/src/tests.rs index 9fb3ece..89ff279 100644 --- a/contracts/parameters-contract/src/tests.rs +++ b/contracts/parameters-contract/src/tests.rs @@ -2,7 +2,10 @@ use crate::{ default_parameters, ParametersContract, ParametersContractClient, ParametersError, ProtocolParameters, }; -use soroban_sdk::{testutils::{Address as _, Events}, Address, Env, IntoVal}; +use soroban_sdk::{ + testutils::{Address as _, Events}, + Address, Env, IntoVal, +}; fn setup() -> (Env, ParametersContractClient<'static>, Address) { let env = Env::default(); @@ -106,13 +109,19 @@ fn test_admin_upgrade_increments_version() { client.initialize_defaults(&admin); assert_eq!(client.get_version(), 1u32); - let wasm_hash = env.deployer().upload_contract_wasm(soroban_sdk::Bytes::from_slice( - &env, - include_bytes!("../../../contracts/test-fixtures/contract.wasm"), - )); + let wasm_hash = env + .deployer() + .upload_contract_wasm(soroban_sdk::Bytes::from_slice( + &env, + include_bytes!("../../../contracts/test-fixtures/contract.wasm"), + )); client.upgrade(&wasm_hash); - let events: soroban_sdk::Vec<(soroban_sdk::Address, soroban_sdk::Vec, soroban_sdk::Val)> = env.events().all(); + let events: soroban_sdk::Vec<( + soroban_sdk::Address, + soroban_sdk::Vec, + soroban_sdk::Val, + )> = env.events().all(); let mut found = false; for e in events.iter() { let topic: soroban_sdk::Symbol = e.1.get_unchecked(0).into_val(&env); diff --git a/contracts/reputation-contract/src/safe_math.rs b/contracts/reputation-contract/src/safe_math.rs index fe5c159..50d53a4 100644 --- a/contracts/reputation-contract/src/safe_math.rs +++ b/contracts/reputation-contract/src/safe_math.rs @@ -1,3 +1,4 @@ +#![allow(dead_code)] use crate::errors::ReputationError; pub fn add_u32(a: u32, b: u32) -> Result { diff --git a/contracts/reputation-contract/src/tests.rs b/contracts/reputation-contract/src/tests.rs index 8a9b64d..4568afa 100644 --- a/contracts/reputation-contract/src/tests.rs +++ b/contracts/reputation-contract/src/tests.rs @@ -302,13 +302,19 @@ fn it_allows_admin_upgrade_and_bumps_version() { client.set_admin(&admin); assert_eq!(client.get_version(), 1u32); - let wasm_hash = env.deployer().upload_contract_wasm(soroban_sdk::Bytes::from_slice( - &env, - include_bytes!("../../../contracts/test-fixtures/contract.wasm"), - )); + let wasm_hash = env + .deployer() + .upload_contract_wasm(soroban_sdk::Bytes::from_slice( + &env, + include_bytes!("../../../contracts/test-fixtures/contract.wasm"), + )); client.upgrade(&wasm_hash); - let events: soroban_sdk::Vec<(soroban_sdk::Address, soroban_sdk::Vec, soroban_sdk::Val)> = env.events().all(); + let events: soroban_sdk::Vec<( + soroban_sdk::Address, + soroban_sdk::Vec, + soroban_sdk::Val, + )> = env.events().all(); let mut found = false; for e in events.iter() { let topic: soroban_sdk::Symbol = e.1.get_unchecked(0).into_val(&env); diff --git a/contracts/vendor-registry-contract/src/lib.rs b/contracts/vendor-registry-contract/src/lib.rs index 2ac730a..f960afd 100644 --- a/contracts/vendor-registry-contract/src/lib.rs +++ b/contracts/vendor-registry-contract/src/lib.rs @@ -227,7 +227,8 @@ impl VendorRegistryContract { /// Upgrade the contract WASM — admin only pub fn upgrade(env: Env, new_wasm_hash: soroban_sdk::BytesN<32>) { - let admin = storage::get_admin(&env).unwrap_or_else(|err| soroban_sdk::panic_with_error!(&env, err)); + let admin = storage::get_admin(&env) + .unwrap_or_else(|err| soroban_sdk::panic_with_error!(&env, err)); admin.require_auth(); let old = storage::get_version(&env).unwrap_or(1u32); diff --git a/contracts/vendor-registry-contract/src/safe_math.rs b/contracts/vendor-registry-contract/src/safe_math.rs index 176d97b..d18b89d 100644 --- a/contracts/vendor-registry-contract/src/safe_math.rs +++ b/contracts/vendor-registry-contract/src/safe_math.rs @@ -1,3 +1,4 @@ +#![allow(dead_code)] use crate::errors::Error; pub fn add_u64(a: u64, b: u64) -> Result { diff --git a/contracts/vendor-registry-contract/src/tests.rs b/contracts/vendor-registry-contract/src/tests.rs index 09254e2..b6ab442 100644 --- a/contracts/vendor-registry-contract/src/tests.rs +++ b/contracts/vendor-registry-contract/src/tests.rs @@ -1,7 +1,7 @@ use super::*; use soroban_sdk::{ testutils::{Address as _, Events, Ledger}, - Address, Env, IntoVal, String, Val, Vec, + Address, Env, IntoVal, String, }; /// Helper function to set up the environment, contract, and test addresses. @@ -440,17 +440,23 @@ fn test_upgrade_rejected_for_non_admin() { #[test] fn test_admin_upgrade_increments_version_and_emits_event() { let env = Env::default(); - let (client, admin, _vendor) = setup(&env); + let (client, _admin, _vendor) = setup(&env); env.mock_all_auths(); assert_eq!(client.get_version(), 1u32); - let wasm_hash = env.deployer().upload_contract_wasm(soroban_sdk::Bytes::from_slice( - &env, - include_bytes!("../../../contracts/test-fixtures/contract.wasm"), - )); + let wasm_hash = env + .deployer() + .upload_contract_wasm(soroban_sdk::Bytes::from_slice( + &env, + include_bytes!("../../../contracts/test-fixtures/contract.wasm"), + )); client.upgrade(&wasm_hash); - let events: soroban_sdk::Vec<(soroban_sdk::Address, soroban_sdk::Vec, soroban_sdk::Val)> = env.events().all(); + let events: soroban_sdk::Vec<( + soroban_sdk::Address, + soroban_sdk::Vec, + soroban_sdk::Val, + )> = env.events().all(); let mut found = false; for e in events.iter() { let topic: soroban_sdk::Symbol = e.1.get_unchecked(0).into_val(&env); diff --git a/contracts/vouching-contract/src/safe_math.rs b/contracts/vouching-contract/src/safe_math.rs index a8fb30f..5edb3c1 100644 --- a/contracts/vouching-contract/src/safe_math.rs +++ b/contracts/vouching-contract/src/safe_math.rs @@ -1,3 +1,4 @@ +#![allow(dead_code)] use crate::errors::VouchingError; pub fn add_u32(a: u32, b: u32) -> Result {