From 026b31476de69e96474e8d860409a20e98a3f5bc Mon Sep 17 00:00:00 2001 From: nanaf6203-bit Date: Wed, 24 Jun 2026 06:37:57 +0000 Subject: [PATCH] fix(identity): resolve KycTier merge markers; chore(style): cargo fmt --all - contracts/identity/lib.rs: pick main side of unresolved conflict markers (lines 227-244) around the KycTier enum, preserving both 5-variant CamelCase and 5-variant underscored naming forms (Tier3Enhanced AND Tier3_Enhanced) because callers (complete_kyc_verification, tests, etc.) already use both styles. - 20 additional files reformatted by `cargo fmt --all`. NOTE: `cargo fmt --all -- --check` STILL exits 1 on this tree. Structural bug in workspace layout: contracts/insurance/src/ declares `mod premium_engine;` inside `mod propchain_insurance { ... }`, but premium_engine.rs lives at the flat src/ level, not at src/propchain_insurance/premium_engine.rs. Same issue in contracts/tax-compliance/src/ for tax_engine / jurisdiction_presets / tax_strategies. Companion PR (or follow-up) needed for the file moves before cargo fmt --check can pass. --- contracts/analytics/src/lib.rs | 53 +- contracts/crowdfunding/src/lib.rs | 27 +- contracts/escrow/src/lib.rs | 100 ++- contracts/escrow/src/tests.rs | 24 +- contracts/fractional/src/lib.rs | 611 +++++++++--------- contracts/governance/src/lib.rs | 43 +- contracts/identity/lib.rs | 8 - contracts/lib/src/lib.rs | 6 - contracts/oracle/src/lib.rs | 157 ++--- contracts/property-management/src/lib.rs | 6 +- contracts/property-token/src/lib.rs | 1 - contracts/staking/src/lib.rs | 241 +++---- contracts/traits/src/oracle.rs | 32 +- contracts/traits/src/property.rs | 2 +- indexer/src/main.rs | 4 +- tests/bridge_chaos.rs | 64 +- tests/bridge_load_tests.rs | 3 +- tests/integration_governance_staking.rs | 56 +- tests/regression/closed_bugs.rs | 8 +- tests/regression/mod.rs | 1 - tests/regression/reinsurance_stats_derives.rs | 3 +- 21 files changed, 742 insertions(+), 708 deletions(-) diff --git a/contracts/analytics/src/lib.rs b/contracts/analytics/src/lib.rs index fd7d4632..10f0398c 100644 --- a/contracts/analytics/src/lib.rs +++ b/contracts/analytics/src/lib.rs @@ -305,7 +305,11 @@ mod propchain_analytics { /// Update portfolio positions for an owner. #[ink(message)] - pub fn set_portfolio_positions(&mut self, owner: AccountId, positions: Vec) { + pub fn set_portfolio_positions( + &mut self, + owner: AccountId, + positions: Vec, + ) { self.ensure_admin(); self.portfolio_positions.insert(owner, &positions); } @@ -345,10 +349,7 @@ mod propchain_analytics { /// Get portfolio rebalancing suggestions for an owner. #[ink(message)] - pub fn get_rebalancing_suggestions( - &self, - owner: AccountId, - ) -> Vec { + pub fn get_rebalancing_suggestions(&self, owner: AccountId) -> Vec { let positions = self.get_portfolio_positions(owner); let total_value: u128 = positions.iter().map(|p| p.value).sum(); if total_value == 0 { @@ -383,9 +384,13 @@ mod propchain_analytics { let current_bips = ((current_value * 10000) / total_value) as u32; let diff = current_bips as i32 - target_bips as i32; let recommendation = if diff > 200 { - String::from("Overweight: consider reducing exposure for this property type.") + String::from( + "Overweight: consider reducing exposure for this property type.", + ) } else if diff < -200 { - String::from("Underweight: consider increasing exposure for this property type.") + String::from( + "Underweight: consider increasing exposure for this property type.", + ) } else { String::from("Aligned with target allocation.") }; @@ -427,8 +432,10 @@ mod propchain_analytics { } else { 0 }; - let trend_bonus = ((trend_total as i32) / (distinct_types.len().max(1) as i32)).clamp(-10, 10) as i8; - let mut score = 50i32 + distinct_bonus as i32 - concentration_penalty as i32 + trend_bonus as i32; + let trend_bonus = + ((trend_total as i32) / (distinct_types.len().max(1) as i32)).clamp(-10, 10) as i8; + let mut score = + 50i32 + distinct_bonus as i32 - concentration_penalty as i32 + trend_bonus as i32; score = score.clamp(0, 100); score as u8 } @@ -474,8 +481,8 @@ mod propchain_analytics { } let block = self.env().block_number(); - let effective_at = block - .saturating_add(propchain_traits::constants::KEY_ROTATION_COOLDOWN_BLOCKS); + let effective_at = + block.saturating_add(propchain_traits::constants::KEY_ROTATION_COOLDOWN_BLOCKS); self.pending_admin_rotation = Some(propchain_traits::KeyRotationRequest { old_account: caller, @@ -558,9 +565,7 @@ mod propchain_analytics { /// Get the pending admin rotation request, if any. #[ink(message)] - pub fn get_pending_admin_rotation( - &self, - ) -> Option { + pub fn get_pending_admin_rotation(&self) -> Option { self.pending_admin_rotation.clone() } } @@ -631,7 +636,10 @@ mod propchain_analytics { }, ); let score = contract.get_portfolio_health_score(owner); - assert!(score < 50, "Expected low score for concentrated, weak trend"); + assert!( + score < 50, + "Expected low score for concentrated, weak trend" + ); } #[ink::test] @@ -681,7 +689,10 @@ mod propchain_analytics { }, ); let score = contract.get_portfolio_health_score(owner); - assert!(score >= 60, "Expected higher score for diversified portfolio"); + assert!( + score >= 60, + "Expected higher score for diversified portfolio" + ); } #[ink::test] @@ -724,7 +735,10 @@ mod propchain_analytics { .iter() .find(|s| s.property_type == propchain_traits::PropertyType::Residential) .expect("Residential suggestion exists"); - assert!(residential_first.target_allocation_bips > residential_first.current_allocation_bips); + assert!( + residential_first.target_allocation_bips + > residential_first.current_allocation_bips + ); assert!(residential_first.recommendation.contains("Underweight")); contract.update_property_type_trend( @@ -751,7 +765,10 @@ mod propchain_analytics { .iter() .find(|s| s.property_type == propchain_traits::PropertyType::Residential) .expect("Residential suggestion exists after trend shift"); - assert!(residential_second.target_allocation_bips < residential_first.target_allocation_bips); + assert!( + residential_second.target_allocation_bips + < residential_first.target_allocation_bips + ); } } } diff --git a/contracts/crowdfunding/src/lib.rs b/contracts/crowdfunding/src/lib.rs index 03be0685..e383cf07 100644 --- a/contracts/crowdfunding/src/lib.rs +++ b/contracts/crowdfunding/src/lib.rs @@ -267,7 +267,7 @@ mod propchain_crowdfunding { pub largest_investment: u128, pub milestone_completion_rate: u32, // in basis points pub days_active: u32, - pub funding_velocity: u128, // tokens per day + pub funding_velocity: u128, // tokens per day pub investor_retention_rate: u32, // in basis points pub risk_score: u32, pub projected_completion_days: u32, @@ -888,7 +888,11 @@ mod propchain_crowdfunding { } #[ink(message)] - pub fn share_campaign(&mut self, campaign_id: u64, platform: String) -> Result<(), CrowdfundingError> { + pub fn share_campaign( + &mut self, + campaign_id: u64, + platform: String, + ) -> Result<(), CrowdfundingError> { let _campaign = self .campaigns .get(campaign_id) @@ -1256,7 +1260,8 @@ mod propchain_crowdfunding { let projected_completion_days = if funding_velocity == 0 { 0 } else { - ((campaign.target_amount.saturating_sub(total_investment)) / funding_velocity) as u32 + ((campaign.target_amount.saturating_sub(total_investment)) / funding_velocity) + as u32 }; Some(CampaignAnalytics { @@ -1320,10 +1325,10 @@ mod propchain_crowdfunding { // Placeholder investment distribution let investment_distribution = vec![ - (1_000, total_investors * 3 / 10), // 0-1k - (10_000, total_investors * 4 / 10), // 1k-10k - (100_000, total_investors * 2 / 10), // 10k-100k - (1_000_000, total_investors * 1 / 10), // 100k+ + (1_000, total_investors * 3 / 10), // 0-1k + (10_000, total_investors * 4 / 10), // 1k-10k + (100_000, total_investors * 2 / 10), // 10k-100k + (1_000_000, total_investors * 1 / 10), // 100k+ ]; Some(InvestorDemographics { @@ -1338,7 +1343,10 @@ mod propchain_crowdfunding { /// Get performance comparison with similar campaigns #[ink(message)] - pub fn get_campaign_performance_comparison(&self, campaign_id: u64) -> Option<(u32, u32, u32)> { + pub fn get_campaign_performance_comparison( + &self, + campaign_id: u64, + ) -> Option<(u32, u32, u32)> { let campaign = self.campaigns.get(campaign_id)?; if self.env().caller() != campaign.creator && self.env().caller() != self.admin { return None; @@ -1426,9 +1434,8 @@ mod tests { use super::*; use ink::env::{test, DefaultEnvironment}; use propchain_crowdfunding::{ - CampaignFilter, CampaignStatus, CrowdfundingError, RealEstateCrowdfunding, RiskRating, CampaignAnalytics, CampaignFilter, CampaignStatus, CampaignSummary, CrowdfundingError, - InvestorDemographics, RiskRating, RealEstateCrowdfunding, + InvestorDemographics, RealEstateCrowdfunding, RiskRating, }; fn setup() -> RealEstateCrowdfunding { diff --git a/contracts/escrow/src/lib.rs b/contracts/escrow/src/lib.rs index da055fc8..185e1ce4 100644 --- a/contracts/escrow/src/lib.rs +++ b/contracts/escrow/src/lib.rs @@ -298,7 +298,10 @@ mod propchain_escrow { impl AdvancedEscrow { /// Constructor #[ink(constructor)] - pub fn new(min_high_value_threshold: u128, tax_compliance_contract: Option) -> Self { + pub fn new( + min_high_value_threshold: u128, + tax_compliance_contract: Option, + ) -> Self { Self { escrows: Mapping::default(), escrow_summaries: Mapping::default(), @@ -394,10 +397,15 @@ mod propchain_escrow { self.analytics.total_created += 1; self.analytics.total_volume = self.analytics.total_volume.saturating_add(amount); self.analytics.total_active += 1; - self.analytics.average_escrow_amount = self.analytics.total_volume / self.analytics.total_created as u128; + self.analytics.average_escrow_amount = + self.analytics.total_volume / self.analytics.total_created as u128; // Track unique participants for participant in &[buyer, seller] { - if !self.analytics_participants.get(participant).unwrap_or(false) { + if !self + .analytics_participants + .get(participant) + .unwrap_or(false) + { self.analytics_participants.insert(participant, &true); self.analytics.unique_participants += 1; } @@ -549,16 +557,16 @@ mod propchain_escrow { if self.env().transfer(collector, withheld_amount).is_err() { return Err(Error::InsufficientFunds); } - final_transfer_amount = final_transfer_amount.saturating_sub(withheld_amount); + final_transfer_amount = + final_transfer_amount.saturating_sub(withheld_amount); } } // ── End Tax Withholding ────────────────────────────────────── // ── Fee Deduction ─────────────────────────────────────────── let fee = if self.fee_rate_bps > 0 && self.fee_recipient.is_some() { - let calculated = final_transfer_amount - .saturating_mul(self.fee_rate_bps as u128) - / 10_000; + let calculated = + final_transfer_amount.saturating_mul(self.fee_rate_bps as u128) / 10_000; if calculated > 0 { let recipient = self.fee_recipient.unwrap(); if self.env().transfer(recipient, calculated).is_err() { @@ -596,7 +604,10 @@ mod propchain_escrow { // Track analytics self.analytics.total_released += 1; self.analytics.total_active = self.analytics.total_active.saturating_sub(1); - self.analytics.total_released_volume = self.analytics.total_released_volume.saturating_add(escrow.deposited_amount); + self.analytics.total_released_volume = self + .analytics + .total_released_volume + .saturating_add(escrow.deposited_amount); // Add audit entry self.add_audit_entry( @@ -619,11 +630,7 @@ mod propchain_escrow { /// Release a partial amount from escrow to the seller. /// The escrow remains active for any remaining balance. #[ink(message)] - pub fn release_funds_partial( - &mut self, - escrow_id: u64, - amount: u128, - ) -> Result<(), Error> { + pub fn release_funds_partial(&mut self, escrow_id: u64, amount: u128) -> Result<(), Error> { non_reentrant!(self, { let caller = self.env().caller(); let mut escrow = self.escrows.get(&escrow_id).ok_or(Error::EscrowNotFound)?; @@ -632,7 +639,12 @@ mod propchain_escrow { return Err(Error::InvalidStatus); } - if amount == 0 || amount > escrow.deposited_amount.saturating_sub(escrow.total_released) { + if amount == 0 + || amount + > escrow + .deposited_amount + .saturating_sub(escrow.total_released) + { return Err(Error::InsufficientFunds); } @@ -670,7 +682,9 @@ mod propchain_escrow { self.escrows.insert(&escrow_id, &escrow); - let remaining = escrow.deposited_amount.saturating_sub(escrow.total_released); + let remaining = escrow + .deposited_amount + .saturating_sub(escrow.total_released); self.add_audit_entry( escrow_id, @@ -1028,7 +1042,10 @@ mod propchain_escrow { } #[ink(message)] - pub fn set_tax_compliance_contract(&mut self, contract: Option) -> Result<(), Error> { + pub fn set_tax_compliance_contract( + &mut self, + contract: Option, + ) -> Result<(), Error> { if self.env().caller() != self.admin { return Err(Error::Unauthorized); } @@ -1140,12 +1157,16 @@ mod propchain_escrow { let mut dispute = self.disputes.get(&escrow_id).ok_or(Error::EscrowNotFound)?; // Track resolution time (using block_timestamp consistently) - let resolution_time = self.env().block_timestamp().saturating_sub(dispute.raised_at); + let resolution_time = self + .env() + .block_timestamp() + .saturating_sub(dispute.raised_at); let total_resolved = self.analytics.total_disputes_resolved; let old_avg = self.analytics.average_dispute_resolution_time; - self.analytics.average_dispute_resolution_time = - (old_avg.saturating_mul(total_resolved).saturating_add(resolution_time)) - / (total_resolved + 1); + self.analytics.average_dispute_resolution_time = (old_avg + .saturating_mul(total_resolved) + .saturating_add(resolution_time)) + / (total_resolved + 1); self.analytics.total_disputes_resolved += 1; dispute.resolved = true; @@ -1155,7 +1176,7 @@ mod propchain_escrow { // Update escrow status back to Active let mut escrow = self.escrows.get(&escrow_id).ok_or(Error::EscrowNotFound)?; escrow.status = EscrowStatus::Active; - escrow.completed_at = None; + escrow.completed_at = None; self.escrows.insert(&escrow_id, &escrow); // Add audit entry @@ -1409,7 +1430,8 @@ mod propchain_escrow { if self.env().transfer(collector, withheld_amount).is_err() { return Err(Error::InsufficientFunds); } - final_transfer_amount = final_transfer_amount.saturating_sub(withheld_amount); + final_transfer_amount = + final_transfer_amount.saturating_sub(withheld_amount); } } // ── End Tax Withholding ────────────────────────────────────── @@ -1616,7 +1638,10 @@ mod propchain_escrow { let caller = self.env().caller(); let escrow = self.escrows.get(&escrow_id).ok_or(Error::EscrowNotFound)?; - if !matches!(escrow.status, EscrowStatus::Released | EscrowStatus::Refunded) { + if !matches!( + escrow.status, + EscrowStatus::Released | EscrowStatus::Refunded + ) { return Err(Error::InvalidStatus); } @@ -1652,11 +1677,10 @@ mod propchain_escrow { multi_sig_config.as_ref(), escrow_id, ); - let compressed_storage_bytes = self.estimate_compressed_storage_bytes( - &summary, - &compressed_audit_logs, - ); - let storage_saved_bytes = detailed_storage_bytes.saturating_sub(compressed_storage_bytes); + let compressed_storage_bytes = + self.estimate_compressed_storage_bytes(&summary, &compressed_audit_logs); + let storage_saved_bytes = + detailed_storage_bytes.saturating_sub(compressed_storage_bytes); self.escrow_summaries.insert(&escrow_id, &summary); self.compressed_audit_logs @@ -1733,7 +1757,10 @@ mod propchain_escrow { /// Returns the multi-sig status summary for an escrow: /// (required_signatures, current_signatures_for_release, current_signatures_for_refund, signers) #[ink(message)] - pub fn get_multi_sig_status(&self, escrow_id: u64) -> Result<(u8, u8, u8, Vec), Error> { + pub fn get_multi_sig_status( + &self, + escrow_id: u64, + ) -> Result<(u8, u8, u8, Vec), Error> { let config = self .multi_sig_configs .get(&escrow_id) @@ -1756,7 +1783,12 @@ mod propchain_escrow { /// Returns whether a specific participant has signed for a given approval type. #[ink(message)] - pub fn has_signed(&self, escrow_id: u64, approval_type: ApprovalType, signer: AccountId) -> bool { + pub fn has_signed( + &self, + escrow_id: u64, + approval_type: ApprovalType, + signer: AccountId, + ) -> bool { self.signatures .get(&(escrow_id, approval_type, signer)) .unwrap_or(false) @@ -1928,9 +1960,7 @@ mod propchain_escrow { if self.fee_rate_bps == 0 || self.fee_recipient.is_none() { return Ok(0); } - let fee = amount - .saturating_mul(self.fee_rate_bps as u128) - / 10_000; + let fee = amount.saturating_mul(self.fee_rate_bps as u128) / 10_000; Ok(fee) } @@ -2184,8 +2214,8 @@ mod propchain_escrow { } total = total.saturating_add( - scale::Encode::encode(&self.audit_logs.get(&escrow_id).unwrap_or_default()) - .len() as u64, + scale::Encode::encode(&self.audit_logs.get(&escrow_id).unwrap_or_default()).len() + as u64, ); total } diff --git a/contracts/escrow/src/tests.rs b/contracts/escrow/src/tests.rs index 4eddab51..9a44e4b0 100644 --- a/contracts/escrow/src/tests.rs +++ b/contracts/escrow/src/tests.rs @@ -31,7 +31,7 @@ pub mod escrow_tests { fn test_set_tax_compliance_contract() { let accounts = default_accounts(); let mut contract = AdvancedEscrow::new(1_000_000, None); - + let result = contract.set_tax_compliance_contract(Some(accounts.charlie)); assert!(result.is_ok()); // Since there is no getter, we just verify it doesn't error. @@ -600,7 +600,10 @@ pub mod escrow_tests { ) .expect("Escrow creation should succeed in test"); - assert_eq!(contract.cleanup_escrow(escrow_id), Err(Error::InvalidStatus)); + assert_eq!( + contract.cleanup_escrow(escrow_id), + Err(Error::InvalidStatus) + ); } #[ink::test] @@ -708,11 +711,17 @@ pub mod escrow_tests { assert!(contract.get_dispute(escrow_id).is_none()); assert!(contract.get_documents(escrow_id).is_empty()); assert!(contract.get_conditions(escrow_id).is_empty()); - assert_eq!(contract.get_signature_count(escrow_id, ApprovalType::Release), 0); - assert_eq!(contract.get_signature_count(escrow_id, ApprovalType::Refund), 0); + assert_eq!( + contract.get_signature_count(escrow_id, ApprovalType::Release), + 0 + ); + assert_eq!( + contract.get_signature_count(escrow_id, ApprovalType::Refund), + 0 + ); - let after_bytes = encoded_len(&summary) as u64 - + encoded_len(&contract.get_audit_trail(escrow_id)) as u64; + let after_bytes = + encoded_len(&summary) as u64 + encoded_len(&contract.get_audit_trail(escrow_id)) as u64; assert!(before_bytes > after_bytes); } @@ -852,7 +861,8 @@ pub mod escrow_tests { &contract .get_escrow_summary(escrow_id) .expect("Summary should be retained after cleanup"), - ) as u64 + encoded_len(&contract.get_audit_trail(escrow_id)) as u64; + ) as u64 + + encoded_len(&contract.get_audit_trail(escrow_id)) as u64; assert!(before_bytes > after_bytes); } diff --git a/contracts/fractional/src/lib.rs b/contracts/fractional/src/lib.rs index 138492ac..461e4bbd 100644 --- a/contracts/fractional/src/lib.rs +++ b/contracts/fractional/src/lib.rs @@ -225,7 +225,6 @@ mod fractional { new_spot_price: u128, } - /// Emitted when a Dutch auction is created #[ink(event)] pub struct DutchAuctionCreated { @@ -490,68 +489,68 @@ mod fractional { shares: u128, ) -> Result<(), FractionalError> { non_reentrant!(self, { - if shares == 0 { - return Err(FractionalError::ZeroAmount); - } - let buyer = self.env().caller(); - let payment = self.env().transferred_value(); + if shares == 0 { + return Err(FractionalError::ZeroAmount); + } + let buyer = self.env().caller(); + let payment = self.env().transferred_value(); - let listing = self - .listings - .get(&(seller, token_id)) - .ok_or(FractionalError::ListingNotFound)?; + let listing = self + .listings + .get(&(seller, token_id)) + .ok_or(FractionalError::ListingNotFound)?; - if shares > listing.shares { - return Err(FractionalError::InsufficientShares); - } + if shares > listing.shares { + return Err(FractionalError::InsufficientShares); + } - let total_price = listing.price_per_share.saturating_mul(shares); - if payment < total_price { - return Err(FractionalError::InsufficientPayment); - } + let total_price = listing.price_per_share.saturating_mul(shares); + if payment < total_price { + return Err(FractionalError::InsufficientPayment); + } - // Transfer shares: deduct from seller, credit buyer - let seller_held = self.balances.get(&(seller, token_id)).unwrap_or(0); - self.balances - .insert(&(seller, token_id), &seller_held.saturating_sub(shares)); + // Transfer shares: deduct from seller, credit buyer + let seller_held = self.balances.get(&(seller, token_id)).unwrap_or(0); + self.balances + .insert(&(seller, token_id), &seller_held.saturating_sub(shares)); - let buyer_held = self.balances.get(&(buyer, token_id)).unwrap_or(0); - self.balances - .insert(&(buyer, token_id), &buyer_held.saturating_add(shares)); - - // Update or remove listing - let remaining = listing.shares.saturating_sub(shares); - if remaining == 0 { - self.listings.remove(&(seller, token_id)); - } else { - let updated = ShareListing { - shares: remaining, - ..listing - }; - self.listings.insert(&(seller, token_id), &updated); - } + let buyer_held = self.balances.get(&(buyer, token_id)).unwrap_or(0); + self.balances + .insert(&(buyer, token_id), &buyer_held.saturating_add(shares)); - // Pay the seller - if self.env().transfer(seller, total_price).is_err() { - // Non-fatal: payment forwarding failed (e.g. in unit tests) - } + // Update or remove listing + let remaining = listing.shares.saturating_sub(shares); + if remaining == 0 { + self.listings.remove(&(seller, token_id)); + } else { + let updated = ShareListing { + shares: remaining, + ..listing + }; + self.listings.insert(&(seller, token_id), &updated); + } - // Analytics: record trade and update holder counts - let price_per_share = listing.price_per_share; - let seller_new_bal = self.balances.get(&(seller, token_id)).unwrap_or(0); - let buyer_new_bal = self.balances.get(&(buyer, token_id)).unwrap_or(0); - self.record_trade(token_id, total_price, price_per_share); - self.update_holder(seller, token_id, seller_new_bal); - self.update_holder(buyer, token_id, buyer_new_bal); - - self.env().emit_event(SharesSold { - seller, - buyer, - token_id, - shares, - total_price, - }); - Ok(()) + // Pay the seller + if self.env().transfer(seller, total_price).is_err() { + // Non-fatal: payment forwarding failed (e.g. in unit tests) + } + + // Analytics: record trade and update holder counts + let price_per_share = listing.price_per_share; + let seller_new_bal = self.balances.get(&(seller, token_id)).unwrap_or(0); + let buyer_new_bal = self.balances.get(&(buyer, token_id)).unwrap_or(0); + self.record_trade(token_id, total_price, price_per_share); + self.update_holder(seller, token_id, seller_new_bal); + self.update_holder(buyer, token_id, buyer_new_bal); + + self.env().emit_event(SharesSold { + seller, + buyer, + token_id, + shares, + total_price, + }); + Ok(()) }) } @@ -564,42 +563,42 @@ mod fractional { shares: u128, ) -> Result { non_reentrant!(self, { - if shares == 0 { - return Err(FractionalError::ZeroAmount); - } - let caller = self.env().caller(); - let held = self.balances.get(&(caller, token_id)).unwrap_or(0); - if held < shares { - return Err(FractionalError::InsufficientShares); - } - - let price = self.last_prices.get(token_id).unwrap_or(0); - let payout = price.saturating_mul(shares); + if shares == 0 { + return Err(FractionalError::ZeroAmount); + } + let caller = self.env().caller(); + let held = self.balances.get(&(caller, token_id)).unwrap_or(0); + if held < shares { + return Err(FractionalError::InsufficientShares); + } - // Burn shares - self.balances - .insert(&(caller, token_id), &held.saturating_sub(shares)); - let total = self.total_shares.get(&token_id).unwrap_or(0); - self.total_shares - .insert(&token_id, &total.saturating_sub(shares)); + let price = self.last_prices.get(token_id).unwrap_or(0); + let payout = price.saturating_mul(shares); - // Pay out (best-effort in unit tests) - if payout > 0 { - let _ = self.env().transfer(caller, payout); - } + // Burn shares + self.balances + .insert(&(caller, token_id), &held.saturating_sub(shares)); + let total = self.total_shares.get(&token_id).unwrap_or(0); + self.total_shares + .insert(&token_id, &total.saturating_sub(shares)); - // Analytics: record redemption as a trade and update holder count - let new_bal = self.balances.get(&(caller, token_id)).unwrap_or(0); - self.record_trade(token_id, payout, price); - self.update_holder(caller, token_id, new_bal); + // Pay out (best-effort in unit tests) + if payout > 0 { + let _ = self.env().transfer(caller, payout); + } - self.env().emit_event(SharesRedeemed { - owner: caller, - token_id, - shares, - payout, - }); - Ok(payout) + // Analytics: record redemption as a trade and update holder count + let new_bal = self.balances.get(&(caller, token_id)).unwrap_or(0); + self.record_trade(token_id, payout, price); + self.update_holder(caller, token_id, new_bal); + + self.env().emit_event(SharesRedeemed { + owner: caller, + token_id, + shares, + payout, + }); + Ok(payout) }) } @@ -622,78 +621,78 @@ mod fractional { min_lp_out: u128, ) -> Result { non_reentrant!(self, { - if share_amount == 0 { - return Err(FractionalError::ZeroAmount); - } - let caller = self.env().caller(); - let value_in = self.env().transferred_value(); - if value_in == 0 { - return Err(FractionalError::ZeroAmount); - } + if share_amount == 0 { + return Err(FractionalError::ZeroAmount); + } + let caller = self.env().caller(); + let value_in = self.env().transferred_value(); + if value_in == 0 { + return Err(FractionalError::ZeroAmount); + } - let held = self.balances.get(&(caller, token_id)).unwrap_or(0); - if held < share_amount { - return Err(FractionalError::InsufficientShares); - } + let held = self.balances.get(&(caller, token_id)).unwrap_or(0); + if held < share_amount { + return Err(FractionalError::InsufficientShares); + } - let lp_minted; - let pool = self.amm_pools.get(token_id); - let updated = match pool { - None => { - // First deposit: LP = sqrt(share_amount * value_in), floored via integer sqrt - lp_minted = Self::isqrt(share_amount.saturating_mul(value_in)); - AmmPool { - share_reserve: share_amount, - value_reserve: value_in, - lp_supply: lp_minted, + let lp_minted; + let pool = self.amm_pools.get(token_id); + let updated = match pool { + None => { + // First deposit: LP = sqrt(share_amount * value_in), floored via integer sqrt + lp_minted = Self::isqrt(share_amount.saturating_mul(value_in)); + AmmPool { + share_reserve: share_amount, + value_reserve: value_in, + lp_supply: lp_minted, + } } - } - Some(p) => { - // Proportional deposit: LP = min(share/share_reserve, value/value_reserve) * lp_supply - let lp_by_share = share_amount - .saturating_mul(p.lp_supply) - .checked_div(p.share_reserve) - .unwrap_or(0); - let lp_by_value = value_in - .saturating_mul(p.lp_supply) - .checked_div(p.value_reserve) - .unwrap_or(0); - lp_minted = lp_by_share.min(lp_by_value); - AmmPool { - share_reserve: p.share_reserve.saturating_add(share_amount), - value_reserve: p.value_reserve.saturating_add(value_in), - lp_supply: p.lp_supply.saturating_add(lp_minted), + Some(p) => { + // Proportional deposit: LP = min(share/share_reserve, value/value_reserve) * lp_supply + let lp_by_share = share_amount + .saturating_mul(p.lp_supply) + .checked_div(p.share_reserve) + .unwrap_or(0); + let lp_by_value = value_in + .saturating_mul(p.lp_supply) + .checked_div(p.value_reserve) + .unwrap_or(0); + lp_minted = lp_by_share.min(lp_by_value); + AmmPool { + share_reserve: p.share_reserve.saturating_add(share_amount), + value_reserve: p.value_reserve.saturating_add(value_in), + lp_supply: p.lp_supply.saturating_add(lp_minted), + } } - } - }; - - if lp_minted < min_lp_out { - return Err(FractionalError::SlippageExceeded); - } + }; - // Lock shares in pool (deduct from caller balance) - self.balances - .insert(&(caller, token_id), &held.saturating_sub(share_amount)); + if lp_minted < min_lp_out { + return Err(FractionalError::SlippageExceeded); + } - // Update spot price - if updated.share_reserve > 0 { - let spot = updated.value_reserve / updated.share_reserve; - self.last_prices.insert(token_id, &spot); - } + // Lock shares in pool (deduct from caller balance) + self.balances + .insert(&(caller, token_id), &held.saturating_sub(share_amount)); - self.amm_pools.insert(token_id, &updated); - let lp_held = self.lp_balances.get(&(caller, token_id)).unwrap_or(0); - self.lp_balances - .insert(&(caller, token_id), &lp_held.saturating_add(lp_minted)); + // Update spot price + if updated.share_reserve > 0 { + let spot = updated.value_reserve / updated.share_reserve; + self.last_prices.insert(token_id, &spot); + } - self.env().emit_event(LiquidityAdded { - provider: caller, - token_id, - shares_added: share_amount, - value_added: value_in, - lp_minted, - }); - Ok(lp_minted) + self.amm_pools.insert(token_id, &updated); + let lp_held = self.lp_balances.get(&(caller, token_id)).unwrap_or(0); + self.lp_balances + .insert(&(caller, token_id), &lp_held.saturating_add(lp_minted)); + + self.env().emit_event(LiquidityAdded { + provider: caller, + token_id, + shares_added: share_amount, + value_added: value_in, + lp_minted, + }); + Ok(lp_minted) }) } @@ -707,66 +706,66 @@ mod fractional { min_value_out: u128, ) -> Result<(u128, u128), FractionalError> { non_reentrant!(self, { - if lp_amount == 0 { - return Err(FractionalError::ZeroAmount); - } - let caller = self.env().caller(); - let lp_held = self.lp_balances.get(&(caller, token_id)).unwrap_or(0); - if lp_held < lp_amount { - return Err(FractionalError::InsufficientLpShares); - } - let pool = self - .amm_pools - .get(token_id) - .ok_or(FractionalError::PoolNotFound)?; - - let shares_out = lp_amount - .saturating_mul(pool.share_reserve) - .checked_div(pool.lp_supply) - .unwrap_or(0); - let value_out = lp_amount - .saturating_mul(pool.value_reserve) - .checked_div(pool.lp_supply) - .unwrap_or(0); - - if shares_out < min_shares_out || value_out < min_value_out { - return Err(FractionalError::SlippageExceeded); - } + if lp_amount == 0 { + return Err(FractionalError::ZeroAmount); + } + let caller = self.env().caller(); + let lp_held = self.lp_balances.get(&(caller, token_id)).unwrap_or(0); + if lp_held < lp_amount { + return Err(FractionalError::InsufficientLpShares); + } + let pool = self + .amm_pools + .get(token_id) + .ok_or(FractionalError::PoolNotFound)?; + + let shares_out = lp_amount + .saturating_mul(pool.share_reserve) + .checked_div(pool.lp_supply) + .unwrap_or(0); + let value_out = lp_amount + .saturating_mul(pool.value_reserve) + .checked_div(pool.lp_supply) + .unwrap_or(0); + + if shares_out < min_shares_out || value_out < min_value_out { + return Err(FractionalError::SlippageExceeded); + } - let updated = AmmPool { - share_reserve: pool.share_reserve.saturating_sub(shares_out), - value_reserve: pool.value_reserve.saturating_sub(value_out), - lp_supply: pool.lp_supply.saturating_sub(lp_amount), - }; + let updated = AmmPool { + share_reserve: pool.share_reserve.saturating_sub(shares_out), + value_reserve: pool.value_reserve.saturating_sub(value_out), + lp_supply: pool.lp_supply.saturating_sub(lp_amount), + }; - // Return shares to caller - let bal = self.balances.get(&(caller, token_id)).unwrap_or(0); - self.balances - .insert(&(caller, token_id), &bal.saturating_add(shares_out)); + // Return shares to caller + let bal = self.balances.get(&(caller, token_id)).unwrap_or(0); + self.balances + .insert(&(caller, token_id), &bal.saturating_add(shares_out)); - self.lp_balances - .insert(&(caller, token_id), &lp_held.saturating_sub(lp_amount)); - self.amm_pools.insert(token_id, &updated); + self.lp_balances + .insert(&(caller, token_id), &lp_held.saturating_sub(lp_amount)); + self.amm_pools.insert(token_id, &updated); - // Update spot price - if updated.share_reserve > 0 { - let spot = updated.value_reserve / updated.share_reserve; - self.last_prices.insert(token_id, &spot); - } + // Update spot price + if updated.share_reserve > 0 { + let spot = updated.value_reserve / updated.share_reserve; + self.last_prices.insert(token_id, &spot); + } - // Return value to caller (best-effort) - if value_out > 0 { - let _ = self.env().transfer(caller, value_out); - } + // Return value to caller (best-effort) + if value_out > 0 { + let _ = self.env().transfer(caller, value_out); + } - self.env().emit_event(LiquidityRemoved { - provider: caller, - token_id, - shares_out, - value_out, - lp_burned: lp_amount, - }); - Ok((shares_out, value_out)) + self.env().emit_event(LiquidityRemoved { + provider: caller, + token_id, + shares_out, + value_out, + lp_burned: lp_amount, + }); + Ok((shares_out, value_out)) }) } @@ -781,76 +780,76 @@ mod fractional { min_value_out: u128, ) -> Result { non_reentrant!(self, { - if shares_in == 0 { - return Err(FractionalError::ZeroAmount); - } - let caller = self.env().caller(); - let held = self.balances.get(&(caller, token_id)).unwrap_or(0); - if held < shares_in { - return Err(FractionalError::InsufficientShares); - } - let pool = self - .amm_pools - .get(token_id) - .ok_or(FractionalError::PoolNotFound)?; - if pool.value_reserve == 0 || pool.share_reserve == 0 { - return Err(FractionalError::InsufficientLiquidity); - } + if shares_in == 0 { + return Err(FractionalError::ZeroAmount); + } + let caller = self.env().caller(); + let held = self.balances.get(&(caller, token_id)).unwrap_or(0); + if held < shares_in { + return Err(FractionalError::InsufficientShares); + } + let pool = self + .amm_pools + .get(token_id) + .ok_or(FractionalError::PoolNotFound)?; + if pool.value_reserve == 0 || pool.share_reserve == 0 { + return Err(FractionalError::InsufficientLiquidity); + } - // 0.3 % fee: effective shares_in after fee - let shares_in_with_fee = shares_in.saturating_mul(9970); - let numerator = shares_in_with_fee.saturating_mul(pool.value_reserve); - let denominator = pool - .share_reserve - .saturating_mul(10000) - .saturating_add(shares_in_with_fee); - let value_out = numerator.checked_div(denominator).unwrap_or(0); - - if value_out < min_value_out { - return Err(FractionalError::SlippageExceeded); - } - if value_out >= pool.value_reserve { - return Err(FractionalError::InsufficientLiquidity); - } + // 0.3 % fee: effective shares_in after fee + let shares_in_with_fee = shares_in.saturating_mul(9970); + let numerator = shares_in_with_fee.saturating_mul(pool.value_reserve); + let denominator = pool + .share_reserve + .saturating_mul(10000) + .saturating_add(shares_in_with_fee); + let value_out = numerator.checked_div(denominator).unwrap_or(0); + + if value_out < min_value_out { + return Err(FractionalError::SlippageExceeded); + } + if value_out >= pool.value_reserve { + return Err(FractionalError::InsufficientLiquidity); + } - let updated = AmmPool { - share_reserve: pool.share_reserve.saturating_add(shares_in), - value_reserve: pool.value_reserve.saturating_sub(value_out), - lp_supply: pool.lp_supply, - }; + let updated = AmmPool { + share_reserve: pool.share_reserve.saturating_add(shares_in), + value_reserve: pool.value_reserve.saturating_sub(value_out), + lp_supply: pool.lp_supply, + }; - // Deduct shares from caller - self.balances - .insert(&(caller, token_id), &held.saturating_sub(shares_in)); - // Burn shares from total supply - let total = self.total_shares.get(token_id).unwrap_or(0); - self.total_shares - .insert(token_id, &total.saturating_sub(shares_in)); - - // Update spot price - let new_spot = if updated.share_reserve > 0 { - let s = updated.value_reserve / updated.share_reserve; - self.last_prices.insert(token_id, &s); - s - } else { - 0 - }; + // Deduct shares from caller + self.balances + .insert(&(caller, token_id), &held.saturating_sub(shares_in)); + // Burn shares from total supply + let total = self.total_shares.get(token_id).unwrap_or(0); + self.total_shares + .insert(token_id, &total.saturating_sub(shares_in)); + + // Update spot price + let new_spot = if updated.share_reserve > 0 { + let s = updated.value_reserve / updated.share_reserve; + self.last_prices.insert(token_id, &s); + s + } else { + 0 + }; - self.amm_pools.insert(token_id, &updated); + self.amm_pools.insert(token_id, &updated); - // Pay caller (best-effort) - if value_out > 0 { - let _ = self.env().transfer(caller, value_out); - } + // Pay caller (best-effort) + if value_out > 0 { + let _ = self.env().transfer(caller, value_out); + } - self.env().emit_event(SharesSwapped { - trader: caller, - token_id, - shares_in, - value_out, - new_spot_price: new_spot, - }); - Ok(value_out) + self.env().emit_event(SharesSwapped { + trader: caller, + token_id, + shares_in, + value_out, + new_spot_price: new_spot, + }); + Ok(value_out) }) } @@ -884,11 +883,7 @@ mod fractional { /// Calculate the current price in a Dutch auction /// Current price = start_price - (elapsed / duration) * (start_price - end_price) - fn calculate_dutch_price( - &self, - auction: &DutchAuction, - current_block: u64, - ) -> u128 { + fn calculate_dutch_price(&self, auction: &DutchAuction, current_block: u64) -> u128 { if current_block >= auction.start_time.saturating_add(auction.duration) { // Auction expired or ended: use end price return auction.end_price; @@ -1018,12 +1013,11 @@ mod fractional { &seller_held.saturating_sub(auction.shares), ); - let buyer_held = self - .balances - .get(&(caller, auction.token_id)) - .unwrap_or(0); - self.balances - .insert(&(caller, auction.token_id), &buyer_held.saturating_add(auction.shares)); + let buyer_held = self.balances.get(&(caller, auction.token_id)).unwrap_or(0); + self.balances.insert( + &(caller, auction.token_id), + &buyer_held.saturating_add(auction.shares), + ); // Mark auction as complete auction.has_bids = true; @@ -1114,8 +1108,8 @@ mod fractional { } let block = self.env().block_number(); - let effective_at = block - .saturating_add(propchain_traits::constants::KEY_ROTATION_COOLDOWN_BLOCKS); + let effective_at = + block.saturating_add(propchain_traits::constants::KEY_ROTATION_COOLDOWN_BLOCKS); self.pending_admin_rotation = Some(propchain_traits::KeyRotationRequest { old_account: caller, @@ -1198,9 +1192,7 @@ mod fractional { /// Get the pending admin rotation request, if any. #[ink(message)] - pub fn get_pending_admin_rotation( - &self, - ) -> Option { + pub fn get_pending_admin_rotation(&self) -> Option { self.pending_admin_rotation.clone() } } @@ -1460,7 +1452,6 @@ mod fractional { assert_eq!(Fractional::isqrt(10_000), 100); } - // ── Dutch Auction Tests ────────────────────────────────────────────── fn charlie() -> AccountId { @@ -1473,9 +1464,7 @@ mod fractional { test::set_caller::(alice()); f.mint_shares(alice(), 1, 100); - let auction_id = f - .create_dutch_auction(1, 50, 1000, 500, 1000) - .unwrap(); + let auction_id = f.create_dutch_auction(1, 50, 1000, 500, 1000).unwrap(); assert_eq!(auction_id, 0); let auction = f.get_dutch_auction(0).unwrap(); @@ -1615,7 +1604,10 @@ mod fractional { test::set_caller::(bob()); test::set_value_transferred::(1_000); // Too low - assert_eq!(f.bid_dutch_auction(0), Err(FractionalError::InsufficientPayment)); + assert_eq!( + f.bid_dutch_auction(0), + Err(FractionalError::InsufficientPayment) + ); } #[ink::test] @@ -1634,7 +1626,10 @@ mod fractional { // Second bid on same auction should fail test::set_caller::(charlie()); test::set_value_transferred::(50_000); - assert_eq!(f.bid_dutch_auction(0), Err(FractionalError::AuctionAlreadyBid)); + assert_eq!( + f.bid_dutch_auction(0), + Err(FractionalError::AuctionAlreadyBid) + ); } #[ink::test] @@ -1642,7 +1637,10 @@ mod fractional { let mut f = Fractional::new(); test::set_caller::(bob()); test::set_value_transferred::(50_000); - assert_eq!(f.bid_dutch_auction(999), Err(FractionalError::AuctionNotFound)); + assert_eq!( + f.bid_dutch_auction(999), + Err(FractionalError::AuctionNotFound) + ); } #[ink::test] @@ -1759,7 +1757,9 @@ mod fractional { f.list_shares_for_sale(1, 50, 10).unwrap(); // Manually lock the guard to simulate a reentrant call - f.reentrancy_guard.enter().expect("first lock should succeed"); + f.reentrancy_guard + .enter() + .expect("first lock should succeed"); test::set_caller::(bob()); // While guard is locked, buy_shares should return ReentrantCall @@ -1785,7 +1785,9 @@ mod fractional { f.add_liquidity(1, 100, 0).unwrap(); // Manually lock the guard to simulate a reentrant call - f.reentrancy_guard.enter().expect("first lock should succeed"); + f.reentrancy_guard + .enter() + .expect("first lock should succeed"); let result = f.swap_shares_for_value(1, 10, 0); assert_eq!( @@ -1805,7 +1807,9 @@ mod fractional { f.mint_shares(alice(), 1, 100); f.set_last_price(1, 5); - f.reentrancy_guard.enter().expect("first lock should succeed"); + f.reentrancy_guard + .enter() + .expect("first lock should succeed"); let result = f.redeem_shares(1, 10); assert_eq!( @@ -1824,7 +1828,9 @@ mod fractional { test::set_caller::(alice()); f.mint_shares(alice(), 1, 1000); - f.reentrancy_guard.enter().expect("first lock should succeed"); + f.reentrancy_guard + .enter() + .expect("first lock should succeed"); test::set_value_transferred::(500); let result = f.add_liquidity(1, 100, 0); @@ -1847,7 +1853,9 @@ mod fractional { test::set_value_transferred::(1000); let lp = f.add_liquidity(1, 200, 0).unwrap(); - f.reentrancy_guard.enter().expect("first lock should succeed"); + f.reentrancy_guard + .enter() + .expect("first lock should succeed"); let result = f.remove_liquidity(1, lp, 0, 0); assert_eq!( @@ -1876,8 +1884,10 @@ mod fractional { let payout2 = f.redeem_shares(1, 50).unwrap(); assert_eq!(payout2, 250); - assert!(!f.reentrancy_guard.is_locked(), "guard must be unlocked after call"); - + assert!( + !f.reentrancy_guard.is_locked(), + "guard must be unlocked after call" + ); } } @@ -1980,5 +1990,4 @@ mod fractional { ); } } - } diff --git a/contracts/governance/src/lib.rs b/contracts/governance/src/lib.rs index cbeabd11..a94c3ea8 100644 --- a/contracts/governance/src/lib.rs +++ b/contracts/governance/src/lib.rs @@ -357,12 +357,12 @@ mod governance { let mut rejected = 0; let mut cancelled = 0; let mut active = 0; - + let mut total_participation_bps: u64 = 0; let mut closed_count = 0; - + let signer_count = self.signers.len() as u64; - + for id in 0..total { if let Some(proposal) = self.proposals.get(id) { match proposal.status { @@ -372,18 +372,24 @@ mod governance { executed += 1; closed_count += 1; if signer_count > 0 { - let total_votes = (proposal.votes_for.saturating_add(proposal.votes_against)) as u64; + let total_votes = + (proposal.votes_for.saturating_add(proposal.votes_against)) + as u64; let bps = total_votes.saturating_mul(10_000) / signer_count; - total_participation_bps = total_participation_bps.saturating_add(bps); + total_participation_bps = + total_participation_bps.saturating_add(bps); } } ProposalStatus::Rejected => { rejected += 1; closed_count += 1; if signer_count > 0 { - let total_votes = (proposal.votes_for.saturating_add(proposal.votes_against)) as u64; + let total_votes = + (proposal.votes_for.saturating_add(proposal.votes_against)) + as u64; let bps = total_votes.saturating_mul(10_000) / signer_count; - total_participation_bps = total_participation_bps.saturating_add(bps); + total_participation_bps = + total_participation_bps.saturating_add(bps); } } ProposalStatus::Cancelled => { @@ -392,21 +398,24 @@ mod governance { ProposalStatus::Expired => { closed_count += 1; if signer_count > 0 { - let total_votes = (proposal.votes_for.saturating_add(proposal.votes_against)) as u64; + let total_votes = + (proposal.votes_for.saturating_add(proposal.votes_against)) + as u64; let bps = total_votes.saturating_mul(10_000) / signer_count; - total_participation_bps = total_participation_bps.saturating_add(bps); + total_participation_bps = + total_participation_bps.saturating_add(bps); } } } } } - + let avg_participation_bps = if closed_count > 0 { (total_participation_bps / closed_count) as u32 } else { 0 }; - + GovernanceAnalytics { total_proposals: total, executed_proposals: executed, @@ -426,7 +435,10 @@ mod governance { /// Returns the participation rate for a specific proposal in basis points. #[ink(message)] pub fn get_proposal_participation(&self, proposal_id: u64) -> Result { - let proposal = self.proposals.get(proposal_id).ok_or(Error::ProposalNotFound)?; + let proposal = self + .proposals + .get(proposal_id) + .ok_or(Error::ProposalNotFound)?; let signer_count = self.signers.len() as u32; if signer_count == 0 { return Ok(0); @@ -885,7 +897,12 @@ mod governance { } /// Internal vote recording logic shared by `vote` and `reveal_vote`. - fn record_vote(&mut self, proposal_id: u64, caller: AccountId, support: bool) -> Result<(), Error> { + fn record_vote( + &mut self, + proposal_id: u64, + caller: AccountId, + support: bool, + ) -> Result<(), Error> { let mut proposal = self .proposals .get(proposal_id) diff --git a/contracts/identity/lib.rs b/contracts/identity/lib.rs index 74d5f14a..ac938441 100644 --- a/contracts/identity/lib.rs +++ b/contracts/identity/lib.rs @@ -224,13 +224,6 @@ pub mod propchain_identity { )] #[cfg_attr(feature = "std", derive(scale_info::TypeInfo))] pub enum KycTier { -<<<<<<< feat/mutation-testing-issue-483 - Tier0Unverified, // No KYC, basic access only - Tier1Basic, // Basic identity verification - Tier2Standard, // Standard KYC with document verification - Tier3Enhanced, // Enhanced due diligence - Tier4Premium, // Premium verification with full background check -======= Tier0Unverified, // No KYC, basic access only Tier1Basic, // Basic identity verification Tier2Standard, // Standard KYC with document verification @@ -241,7 +234,6 @@ pub mod propchain_identity { Tier2_Standard, // Standard KYC with document verification Tier3_Enhanced, // Enhanced due diligence Tier4_Premium, // Premium verification with full background check ->>>>>>> main } /// KYC Tier privileges diff --git a/contracts/lib/src/lib.rs b/contracts/lib/src/lib.rs index 9a57ff7d..ee6a462a 100644 --- a/contracts/lib/src/lib.rs +++ b/contracts/lib/src/lib.rs @@ -120,10 +120,6 @@ pub mod propchain_contracts { } } - - - - /// Property Registry contract #[ink(storage)] pub struct PropertyRegistry { @@ -202,8 +198,6 @@ pub mod propchain_contracts { reentrancy_guard: ReentrancyGuard, } - - /// Escrow information #[derive( Debug, Clone, PartialEq, scale::Encode, scale::Decode, ink::storage::traits::StorageLayout, diff --git a/contracts/oracle/src/lib.rs b/contracts/oracle/src/lib.rs index d8795703..e0e64334 100644 --- a/contracts/oracle/src/lib.rs +++ b/contracts/oracle/src/lib.rs @@ -484,7 +484,8 @@ mod propchain_oracle { fallback_source_ids: Vec::new(), // Auto-slash configuration (Issue #497) auto_slash_on_staleness: false, - auto_slash_staleness_threshold: propchain_traits::constants::DEFAULT_MAX_PRICE_STALENESS, + auto_slash_staleness_threshold: + propchain_traits::constants::DEFAULT_MAX_PRICE_STALENESS, auto_slash_on_deviation: false, auto_slash_deviation_threshold_bps: 2000, // 20% default auto_slash_on_missed_updates: false, @@ -856,7 +857,8 @@ mod propchain_oracle { action: action.clone(), votes_for: 0, votes_against: 0, - voting_end: now.saturating_add(self.governance_params.governance_voting_period_blocks), + voting_end: now + .saturating_add(self.governance_params.governance_voting_period_blocks), executed: false, created_at: now, }; @@ -919,10 +921,7 @@ mod propchain_oracle { /// Execute a governance proposal after voting ends. #[ink(message)] - pub fn execute_governance_proposal( - &mut self, - proposal_id: u64, - ) -> Result<(), OracleError> { + pub fn execute_governance_proposal(&mut self, proposal_id: u64) -> Result<(), OracleError> { let mut proposal = self .governance_proposals .get(&proposal_id) @@ -1022,10 +1021,8 @@ mod propchain_oracle { // ── Track per-source participation (Issue #497) ────────────────── // Build the set of sources that responded this round. - let responding: ink::prelude::collections::BTreeSet = prices - .iter() - .map(|p| p.source.clone()) - .collect(); + let responding: ink::prelude::collections::BTreeSet = + prices.iter().map(|p| p.source.clone()).collect(); // Update last-report time for responding sources; increment // missed-update counter for non-responding active sources. @@ -1037,7 +1034,8 @@ mod propchain_oracle { self.source_missed_updates.insert(sid, &0); } else { let missed = self.source_missed_updates.get(sid).unwrap_or(0); - self.source_missed_updates.insert(sid, &missed.saturating_add(1)); + self.source_missed_updates + .insert(sid, &missed.saturating_add(1)); } } @@ -1217,9 +1215,7 @@ mod propchain_oracle { SlashingSeverity::Critical => self.slashing_config.critical_reputation_penalty, }; - let slash_amount = current_stake - .saturating_mul(slash_bps as u128) - / 10_000; + let slash_amount = current_stake.saturating_mul(slash_bps as u128) / 10_000; let remaining_stake = current_stake.saturating_sub(slash_amount); // Apply stake slash @@ -1245,9 +1241,11 @@ mod propchain_oracle { // Update running totals let current_count = self.slashing_counts.get(&source_id).unwrap_or(0); - self.slashing_counts.insert(&source_id, &(current_count + 1)); + self.slashing_counts + .insert(&source_id, &(current_count + 1)); let current_amount = self.slashed_amounts.get(&source_id).unwrap_or(0); - self.slashed_amounts.insert(&source_id, ¤t_amount.saturating_add(slash_amount)); + self.slashed_amounts + .insert(&source_id, ¤t_amount.saturating_add(slash_amount)); // Auto-suspend if slashing count exceeds threshold if current_count + 1 >= self.slashing_config.suspension_threshold { @@ -1314,10 +1312,7 @@ mod propchain_oracle { /// Update slashing configuration parameters (admin only). #[ink(message)] - pub fn set_slashing_config( - &mut self, - config: SlashingConfig, - ) -> Result<(), OracleError> { + pub fn set_slashing_config(&mut self, config: SlashingConfig) -> Result<(), OracleError> { self.ensure_admin()?; self.slashing_config = config; Ok(()) @@ -1507,20 +1502,19 @@ mod propchain_oracle { if !self.fallback_source_ids.contains(&source.id) { self.fallback_source_ids.push(source.id.clone()); - self.fallback_source_ids - .sort_by(|a, b| { - let pa = self - .fallback_sources - .get(a) - .map(|s| s.priority) - .unwrap_or(999); - let pb = self - .fallback_sources - .get(b) - .map(|s| s.priority) - .unwrap_or(999); - pa.cmp(&pb) - }); + self.fallback_source_ids.sort_by(|a, b| { + let pa = self + .fallback_sources + .get(a) + .map(|s| s.priority) + .unwrap_or(999); + let pb = self + .fallback_sources + .get(b) + .map(|s| s.priority) + .unwrap_or(999); + pa.cmp(&pb) + }); } self.env().emit_event(FallbackSourceAdded { @@ -1803,10 +1797,7 @@ mod propchain_oracle { /// When approval count reaches `multisig_threshold`, the proposal is /// executed automatically (source added or removed). #[ink(message)] - pub fn approve_source_proposal( - &mut self, - proposal_id: u64, - ) -> Result { + pub fn approve_source_proposal(&mut self, proposal_id: u64) -> Result { let caller = self.env().caller(); if !self.multisig_signers.contains(&caller) { return Err(OracleError::Unauthorized); @@ -1845,18 +1836,12 @@ mod propchain_oracle { /// Get a source management proposal by ID. #[ink(message)] - pub fn get_source_proposal( - &self, - proposal_id: u64, - ) -> Option { + pub fn get_source_proposal(&self, proposal_id: u64) -> Option { self.source_proposals.get(&proposal_id) } /// Internal: execute a source management proposal. - fn execute_source_proposal( - &mut self, - proposal_id: u64, - ) -> Result<(), OracleError> { + fn execute_source_proposal(&mut self, proposal_id: u64) -> Result<(), OracleError> { let mut proposal = self .source_proposals .get(&proposal_id) @@ -1916,7 +1901,10 @@ mod propchain_oracle { missed_update_count: u32, ) -> Result<(), OracleError> { self.ensure_admin()?; - if staleness_threshold_secs == 0 || deviation_threshold_bps == 0 || missed_update_count == 0 { + if staleness_threshold_secs == 0 + || deviation_threshold_bps == 0 + || missed_update_count == 0 + { return Err(OracleError::InvalidParameters); } self.auto_slash_on_staleness = on_staleness; @@ -1932,9 +1920,7 @@ mod propchain_oracle { /// (on_staleness, staleness_secs, on_deviation, deviation_bps, /// on_missed_updates, missed_count) #[ink(message)] - pub fn get_auto_slash_config( - &self, - ) -> (bool, u64, bool, u32, bool, u32) { + pub fn get_auto_slash_config(&self) -> (bool, u64, bool, u32, bool, u32) { ( self.auto_slash_on_staleness, self.auto_slash_staleness_threshold, @@ -1975,10 +1961,7 @@ mod propchain_oracle { for source_id in sources { // ── Staleness check ─────────────────────────────────────────── if self.auto_slash_on_staleness { - let last_report = self - .source_last_report_time - .get(&source_id) - .unwrap_or(0); + let last_report = self.source_last_report_time.get(&source_id).unwrap_or(0); if last_report > 0 && now.saturating_sub(last_report) > self.auto_slash_staleness_threshold { @@ -1993,10 +1976,7 @@ mod propchain_oracle { // ── Missed updates check ────────────────────────────────────── if self.auto_slash_on_missed_updates { - let missed = self - .source_missed_updates - .get(&source_id) - .unwrap_or(0); + let missed = self.source_missed_updates.get(&source_id).unwrap_or(0); if missed >= self.auto_slash_missed_update_count { let _ = self.internal_auto_slash( source_id.clone(), @@ -2015,10 +1995,7 @@ mod propchain_oracle { // If the source's last_updated price deviates from consensus, slash. if let Some(source) = self.oracle_sources.get(&source_id) { // Check staleness of last reported value as proxy for participation - let last_report = self - .source_last_report_time - .get(&source_id) - .unwrap_or(0); + let last_report = self.source_last_report_time.get(&source_id).unwrap_or(0); // Only check sources that reported in this round if now.saturating_sub(last_report) <= self.auto_slash_staleness_threshold { // We don't have per-source reported price in current storage; @@ -2041,10 +2018,7 @@ mod propchain_oracle { let current_stake = self.source_stakes.get(&source_id).unwrap_or(0); if current_stake == 0 { // Slash reputation only if no stake - let current_rep = self - .source_reputations - .get(&source_id) - .unwrap_or(500); + let current_rep = self.source_reputations.get(&source_id).unwrap_or(500); let penalty = match severity { SlashingSeverity::Minor => self.slashing_config.minor_reputation_penalty, SlashingSeverity::Moderate => self.slashing_config.moderate_reputation_penalty, @@ -2200,7 +2174,10 @@ mod propchain_oracle { if self.min_update_interval_blocks == 0 { return Ok(()); // No frequency limit } - let last = self.last_source_update.get(&source_id.to_string()).unwrap_or(0); + let last = self + .last_source_update + .get(&source_id.to_string()) + .unwrap_or(0); let current = self.env().block_number() as u64; if last > 0 && current.saturating_sub(last) < self.min_update_interval_blocks { return Err(OracleError::RequestPending); @@ -2556,7 +2533,11 @@ mod propchain_oracle { Ok((avg_bp / 100).min(100) as u32) } - fn collect_historical_window(&self, property_id: u64, window_days: u32) -> Vec { + fn collect_historical_window( + &self, + property_id: u64, + window_days: u32, + ) -> Vec { let history = self .historical_valuations .get(&property_id) @@ -2629,10 +2610,7 @@ mod propchain_oracle { } } - fn compute_trend_metrics( - &self, - property_id: u64, - ) -> Result { + fn compute_trend_metrics(&self, property_id: u64) -> Result { let current = self.get_property_valuation(property_id)?; let window_7d = self.collect_historical_window(property_id, 7); let window_30d = self.collect_historical_window(property_id, 30); @@ -2776,10 +2754,7 @@ mod propchain_oracle { }; // Get existing snapshots - let mut snapshots = self - .oracle_snapshots - .get(&property_id) - .unwrap_or_default(); + let mut snapshots = self.oracle_snapshots.get(&property_id).unwrap_or_default(); // Add new snapshot snapshots.push(snapshot.clone()); @@ -2830,10 +2805,7 @@ mod propchain_oracle { }; // Get existing history for this source - let mut history = self - .source_history - .get(&source_id) - .unwrap_or_default(); + let mut history = self.source_history.get(&source_id).unwrap_or_default(); // Add new entry history.push(entry); @@ -2892,11 +2864,7 @@ mod propchain_oracle { /// Get source history for a specific oracle source #[ink(message)] - pub fn get_source_history( - &self, - source_id: String, - limit: u32, - ) -> Vec { + pub fn get_source_history(&self, source_id: String, limit: u32) -> Vec { self.source_history .get(&source_id) .unwrap_or_default() @@ -2913,7 +2881,8 @@ mod propchain_oracle { property_id: u64, days_lookback: u32, ) -> Result { - let snapshots = self.oracle_snapshots + let snapshots = self + .oracle_snapshots .get(&property_id) .ok_or(OracleError::PropertyNotFound)?; @@ -2968,7 +2937,9 @@ mod propchain_oracle { let trend_direction = if relevant_data.len() > 1 { let first = relevant_data.first().unwrap().valuation as i128; let last = relevant_data.last().unwrap().valuation as i128; - ((last - first) / (relevant_data.len() as i128)).max(-100).min(100) as i32 + ((last - first) / (relevant_data.len() as i128)) + .max(-100) + .min(100) as i32 } else { 0 }; @@ -3085,20 +3056,12 @@ mod propchain_oracle { } #[ink(message)] - fn get_oracle_snapshots( - &self, - property_id: u64, - limit: u32, - ) -> Vec { + fn get_oracle_snapshots(&self, property_id: u64, limit: u32) -> Vec { self.get_oracle_snapshots(property_id, limit) } #[ink(message)] - fn get_source_history( - &self, - source_id: String, - limit: u32, - ) -> Vec { + fn get_source_history(&self, source_id: String, limit: u32) -> Vec { self.get_source_history(source_id, limit) } diff --git a/contracts/property-management/src/lib.rs b/contracts/property-management/src/lib.rs index 28ebe73c..f870182f 100644 --- a/contracts/property-management/src/lib.rs +++ b/contracts/property-management/src/lib.rs @@ -1191,7 +1191,11 @@ mod property_management { #[ink(message)] pub fn vote(&mut self, proposal_id: u64, support: bool) -> Result<(), Error> { let voter = self.env().caller(); - if self.proposal_votes.get((proposal_id, voter)).unwrap_or(false) { + if self + .proposal_votes + .get((proposal_id, voter)) + .unwrap_or(false) + { return Err(Error::InvalidStatus); } diff --git a/contracts/property-token/src/lib.rs b/contracts/property-token/src/lib.rs index d6d3d8a4..22589c17 100644 --- a/contracts/property-token/src/lib.rs +++ b/contracts/property-token/src/lib.rs @@ -128,7 +128,6 @@ pub mod property_token { snapshot_counter: Mapping, snapshots: Mapping<(TokenId, u64), Snapshot>, account_snapshots: Mapping<(AccountId, TokenId, u64), u128>, // (account, token_id, snapshot_id) -> balance - } // Data types extracted to types.rs (Issue #101) diff --git a/contracts/staking/src/lib.rs b/contracts/staking/src/lib.rs index be29331f..89128030 100644 --- a/contracts/staking/src/lib.rs +++ b/contracts/staking/src/lib.rs @@ -596,92 +596,89 @@ mod staking { Ok(()) } - /// Unstake tokens. If called before the lock period expires, a penalty -/// of `early_withdrawal_penalty_bps` is deducted from the returned amount. -/// The penalty amount is retained in the reward pool. -/// If vesting schedule exists, unvested rewards are returned to the reward pool. + /// Unstake tokens. If called before the lock period expires, a penalty + /// of `early_withdrawal_penalty_bps` is deducted from the returned amount. + /// The penalty amount is retained in the reward pool. + /// If vesting schedule exists, unvested rewards are returned to the reward pool. #[ink(message)] pub fn unstake(&mut self) -> Result<(), Error> { - propchain_traits::non_reentrant!(self, { - let caller = self.env().caller(); - let stake = self.stakes.get(caller).ok_or(Error::StakeNotFound)?; - - let now = self.env().block_number() as u64; - let amount = stake.amount; - let is_early = now < stake.lock_until; - - // Calculate penalty for early withdrawal (zero for on-time or flexible) - let penalty = if is_early && stake.lock_period != LockPeriod::Flexible { - amount - .saturating_mul(self.early_withdrawal_penalty_bps) - .saturating_div(constants::BASIS_POINTS_DENOMINATOR as u128) - } else { - 0 - }; - - let amount_returned = amount.saturating_sub(penalty); - - // Return unvested rewards to the pool if vesting schedule exists - if let Some(vesting) = stake.vesting_schedule { - let unvested = vesting.total_amount.saturating_sub(vesting.vested_amount); - self.reward_pool = self.reward_pool.saturating_add(unvested); - } + propchain_traits::non_reentrant!(self, { + let caller = self.env().caller(); + let stake = self.stakes.get(caller).ok_or(Error::StakeNotFound)?; - // Remove governance power - self.remove_governance_power(&stake); + let now = self.env().block_number() as u64; + let amount = stake.amount; + let is_early = now < stake.lock_until; + + // Calculate penalty for early withdrawal (zero for on-time or flexible) + let penalty = if is_early && stake.lock_period != LockPeriod::Flexible { + amount + .saturating_mul(self.early_withdrawal_penalty_bps) + .saturating_div(constants::BASIS_POINTS_DENOMINATOR as u128) + } else { + 0 + }; - self.stakes.remove(caller); - self.total_staked = self.total_staked.saturating_sub(amount); + let amount_returned = amount.saturating_sub(penalty); - // Penalty stays in the reward pool to benefit remaining stakers - if penalty > 0 { - self.reward_pool = self.reward_pool.saturating_add(penalty); - } + // Return unvested rewards to the pool if vesting schedule exists + if let Some(vesting) = stake.vesting_schedule { + let unvested = vesting.total_amount.saturating_sub(vesting.vested_amount); + self.reward_pool = self.reward_pool.saturating_add(unvested); + } - // Remove from staker list - if let Some(pos) = self.staker_list.iter().position(|s| *s == caller) { - self.staker_list.swap_remove(pos); - } + // Remove governance power + self.remove_governance_power(&stake); - if is_early && stake.lock_period != LockPeriod::Flexible { - self.env().emit_event(EarlyWithdrawal { - staker: caller, - amount_returned, - penalty, - }); - } else { - self.env().emit_event(Unstaked { - staker: caller, - amount, - }); - } + self.stakes.remove(caller); + self.total_staked = self.total_staked.saturating_sub(amount); + + // Penalty stays in the reward pool to benefit remaining stakers + if penalty > 0 { + self.reward_pool = self.reward_pool.saturating_add(penalty); + } + + // Remove from staker list + if let Some(pos) = self.staker_list.iter().position(|s| *s == caller) { + self.staker_list.swap_remove(pos); + } + + if is_early && stake.lock_period != LockPeriod::Flexible { + self.env().emit_event(EarlyWithdrawal { + staker: caller, + amount_returned, + penalty, + }); + } else { + self.env().emit_event(Unstaked { + staker: caller, + amount, + }); + } - Ok(()) - }) + Ok(()) + }) } /// Update the early withdrawal penalty rate. Admin only. -/// `penalty_bps` must not exceed `MAX_EARLY_WITHDRAWAL_PENALTY_BPS`. -/// -#[ink(message)] -pub fn set_early_withdrawal_penalty( - &mut self, - penalty_bps: u128, -) -> Result<(), Error> { - if self.env().caller() != self.admin { - return Err(Error::Unauthorized); - } - if penalty_bps > constants::MAX_EARLY_WITHDRAWAL_PENALTY_BPS { - return Err(Error::InvalidConfig); - } - self.early_withdrawal_penalty_bps = penalty_bps; - Ok(()) -} + /// `penalty_bps` must not exceed `MAX_EARLY_WITHDRAWAL_PENALTY_BPS`. + /// + #[ink(message)] + pub fn set_early_withdrawal_penalty(&mut self, penalty_bps: u128) -> Result<(), Error> { + if self.env().caller() != self.admin { + return Err(Error::Unauthorized); + } + if penalty_bps > constants::MAX_EARLY_WITHDRAWAL_PENALTY_BPS { + return Err(Error::InvalidConfig); + } + self.early_withdrawal_penalty_bps = penalty_bps; + Ok(()) + } -/// Get the current early withdrawal penalty rate in basis points. -#[ink(message)] -pub fn get_early_withdrawal_penalty_bps(&self) -> u128 { - self.early_withdrawal_penalty_bps -} + /// Get the current early withdrawal penalty rate in basis points. + #[ink(message)] + pub fn get_early_withdrawal_penalty_bps(&self) -> u128 { + self.early_withdrawal_penalty_bps + } /// Claim accumulated rewards. #[ink(message)] @@ -736,7 +733,10 @@ pub fn get_early_withdrawal_penalty_bps(&self) -> u128 { // Update governance power let power_holder = stake.governance_delegate.unwrap_or(stake.staker); let current_power = self.governance_power.get(power_holder).unwrap_or(0); - self.governance_power.insert(power_holder, ¤t_power.saturating_add(claimable_amount)); + self.governance_power.insert( + power_holder, + ¤t_power.saturating_add(claimable_amount), + ); stake.staked_at = now; stake.reward_debt = self.acc_reward_per_share; @@ -929,11 +929,7 @@ pub fn get_early_withdrawal_penalty_bps(&self) -> u128 { /// Cast a vote on an active parameter proposal, weighted by the /// caller's current governance power. #[ink(message)] - pub fn vote_on_proposal( - &mut self, - proposal_id: u64, - support: bool, - ) -> Result<(), Error> { + pub fn vote_on_proposal(&mut self, proposal_id: u64, support: bool) -> Result<(), Error> { let caller = self.env().caller(); let weight = self.governance_power.get(caller).unwrap_or(0); if weight == 0 { @@ -1006,16 +1002,14 @@ pub fn get_early_withdrawal_penalty_bps(&self) -> u128 { if total_votes < quorum_required { proposal.status = ProposalStatus::Rejected; self.param_proposals.insert(proposal_id, &proposal); - self.env() - .emit_event(ParamProposalRejected { proposal_id }); + self.env().emit_event(ParamProposalRejected { proposal_id }); return Err(Error::QuorumNotReached); } if proposal.votes_for <= proposal.votes_against { proposal.status = ProposalStatus::Rejected; self.param_proposals.insert(proposal_id, &proposal); - self.env() - .emit_event(ParamProposalRejected { proposal_id }); + self.env().emit_event(ParamProposalRejected { proposal_id }); return Ok(()); } @@ -1191,17 +1185,14 @@ pub fn get_early_withdrawal_penalty_bps(&self) -> u128 { / constants::REWARD_RATE_PRECISION / 5_256_000; // blocks per year - let commission = gross_reward - .saturating_mul(info.commission_rate as u128) - / BPS_DENOMINATOR as u128; + let commission = + gross_reward.saturating_mul(info.commission_rate as u128) / BPS_DENOMINATOR as u128; let net_reward = gross_reward.saturating_sub(commission); info.accumulated_commission = info.accumulated_commission.saturating_add(commission); - info.acc_reward_per_share = info.acc_reward_per_share.saturating_add( - net_reward - .saturating_mul(REWARD_PRECISION) - / info.total_delegated, - ); + info.acc_reward_per_share = info + .acc_reward_per_share + .saturating_add(net_reward.saturating_mul(REWARD_PRECISION) / info.total_delegated); info.last_reward_block = now; self.validators.insert(validator, &info); } @@ -1222,21 +1213,16 @@ pub fn get_early_withdrawal_penalty_bps(&self) -> u128 { .saturating_mul(blocks) / constants::REWARD_RATE_PRECISION / 5_256_000; - let commission = gross - .saturating_mul(info.commission_rate as u128) - / BPS_DENOMINATOR as u128; + let commission = + gross.saturating_mul(info.commission_rate as u128) / BPS_DENOMINATOR as u128; let net = gross.saturating_sub(commission); - info.acc_reward_per_share.saturating_add( - net.saturating_mul(REWARD_PRECISION) / info.total_delegated, - ) + info.acc_reward_per_share + .saturating_add(net.saturating_mul(REWARD_PRECISION) / info.total_delegated) } else { info.acc_reward_per_share }; - (record - .amount - .saturating_mul(projected_acc) - / REWARD_PRECISION) + (record.amount.saturating_mul(projected_acc) / REWARD_PRECISION) .saturating_sub(record.reward_debt) } @@ -1328,7 +1314,8 @@ pub fn get_early_withdrawal_penalty_bps(&self) -> u128 { } info.is_active = true; self.validators.insert(caller, &info); - self.env().emit_event(ValidatorReactivated { validator: caller }); + self.env() + .emit_event(ValidatorReactivated { validator: caller }); Ok(()) } @@ -1349,10 +1336,7 @@ pub fn get_early_withdrawal_penalty_bps(&self) -> u128 { info.self_stake = info.self_stake.saturating_sub(self_slash); // Slash each delegator - let delegators = self - .validator_delegators - .get(validator) - .unwrap_or_default(); + let delegators = self.validator_delegators.get(validator).unwrap_or_default(); let mut total_delegated_reduction: u128 = 0; for delegator in &delegators { let key = (*delegator, validator); @@ -1401,11 +1385,7 @@ pub fn get_early_withdrawal_penalty_bps(&self) -> u128 { /// Delegate `amount` tokens to `validator`. #[ink(message)] - pub fn delegate( - &mut self, - validator: AccountId, - amount: u128, - ) -> Result<(), Error> { + pub fn delegate(&mut self, validator: AccountId, amount: u128) -> Result<(), Error> { propchain_traits::non_reentrant!(self, { let caller = self.env().caller(); let info = self @@ -1425,10 +1405,8 @@ pub fn get_early_withdrawal_penalty_bps(&self) -> u128 { self.update_validator_rewards(validator); let info = self.validators.get(validator).unwrap(); - let reward_debt = info - .acc_reward_per_share - .saturating_mul(amount) - / REWARD_PRECISION; + let reward_debt = + info.acc_reward_per_share.saturating_mul(amount) / REWARD_PRECISION; let record = DelegationRecord { delegator: caller, @@ -1440,10 +1418,7 @@ pub fn get_early_withdrawal_penalty_bps(&self) -> u128 { self.delegations.insert((caller, validator), &record); // Update secondary indices - let mut delegators = self - .validator_delegators - .get(validator) - .unwrap_or_default(); + let mut delegators = self.validator_delegators.get(validator).unwrap_or_default(); delegators.push(caller); self.validator_delegators.insert(validator, &delegators); self.delegator_validator.insert(caller, &validator); @@ -1452,8 +1427,7 @@ pub fn get_early_withdrawal_penalty_bps(&self) -> u128 { let mut info = self.validators.get(validator).unwrap(); info.total_delegated = info.total_delegated.saturating_add(amount); self.validators.insert(validator, &info); - self.total_delegated_stake = - self.total_delegated_stake.saturating_add(amount); + self.total_delegated_stake = self.total_delegated_stake.saturating_add(amount); self.env().emit_event(StakeDelegated { delegator: caller, @@ -1487,9 +1461,8 @@ pub fn get_early_withdrawal_penalty_bps(&self) -> u128 { info.total_delegated = info.total_delegated.saturating_sub(record.amount); self.validators.insert(validator, &info); - self.total_delegated_stake = self - .total_delegated_stake - .saturating_sub(record.amount); + self.total_delegated_stake = + self.total_delegated_stake.saturating_sub(record.amount); self.env().emit_event(UndelegationInitiated { delegator: caller, @@ -1522,10 +1495,7 @@ pub fn get_early_withdrawal_penalty_bps(&self) -> u128 { self.delegator_validator.remove(caller); // Remove from validator_delegators list - let mut delegators = self - .validator_delegators - .get(validator) - .unwrap_or_default(); + let mut delegators = self.validator_delegators.get(validator).unwrap_or_default(); if let Some(pos) = delegators.iter().position(|d| *d == caller) { delegators.swap_remove(pos); } @@ -1541,10 +1511,7 @@ pub fn get_early_withdrawal_penalty_bps(&self) -> u128 { /// Claim pending delegation rewards (net of validator commission). #[ink(message)] - pub fn claim_delegation_rewards( - &mut self, - validator: AccountId, - ) -> Result { + pub fn claim_delegation_rewards(&mut self, validator: AccountId) -> Result { propchain_traits::non_reentrant!(self, { let caller = self.env().caller(); let record = self @@ -1566,10 +1533,8 @@ pub fn get_early_withdrawal_penalty_bps(&self) -> u128 { self.reward_pool = self.reward_pool.saturating_sub(reward); let mut record = self.delegations.get((caller, validator)).unwrap(); - record.reward_debt = info - .acc_reward_per_share - .saturating_mul(record.amount) - / REWARD_PRECISION; + record.reward_debt = + info.acc_reward_per_share.saturating_mul(record.amount) / REWARD_PRECISION; self.delegations.insert((caller, validator), &record); self.env().emit_event(DelegationRewardsClaimed { diff --git a/contracts/traits/src/oracle.rs b/contracts/traits/src/oracle.rs index afc9b4d3..4cf8579a 100644 --- a/contracts/traits/src/oracle.rs +++ b/contracts/traits/src/oracle.rs @@ -329,12 +329,12 @@ pub struct MarketTrend { )] pub struct OracleDataSnapshot { pub property_id: u64, - pub source_id: String, // Which oracle source provided this data - pub valuation: u128, // The valuation value - pub timestamp: u64, // When this snapshot was captured - pub confidence_score: u32, // Confidence in this data (0-100) - pub valuation_method: ValuationMethod, // How the valuation was determined - pub is_anomaly: bool, // Flag if detected as anomaly + pub source_id: String, // Which oracle source provided this data + pub valuation: u128, // The valuation value + pub timestamp: u64, // When this snapshot was captured + pub confidence_score: u32, // Confidence in this data (0-100) + pub valuation_method: ValuationMethod, // How the valuation was determined + pub is_anomaly: bool, // Flag if detected as anomaly } /// Historical entry for a specific oracle source @@ -347,9 +347,9 @@ pub struct SourceHistoryEntry { pub timestamp: u64, pub valuation: u128, pub property_id: u64, - pub success: bool, // Whether this update was successful + pub success: bool, // Whether this update was successful pub confidence_score: u32, - pub update_count: u32, // How many updates this source has made + pub update_count: u32, // How many updates this source has made } /// Statistics calculated from historical oracle data @@ -366,8 +366,8 @@ pub struct OracleHistoryStatistics { pub data_points: u32, pub period_start: u64, pub period_end: u64, - pub volatility_percentage: u32, // Volatility as percentage - pub trend_direction: i32, // Positive (upward) or negative (downward) + pub volatility_percentage: u32, // Volatility as percentage + pub trend_direction: i32, // Positive (upward) or negative (downward) } /// ======================================================================== @@ -410,19 +410,11 @@ pub trait Oracle { /// Get oracle data snapshots for a property #[ink(message)] - fn get_oracle_snapshots( - &self, - property_id: u64, - limit: u32, - ) -> Vec; + fn get_oracle_snapshots(&self, property_id: u64, limit: u32) -> Vec; /// Get history for a specific oracle source #[ink(message)] - fn get_source_history( - &self, - source_id: String, - limit: u32, - ) -> Vec; + fn get_source_history(&self, source_id: String, limit: u32) -> Vec; /// Get historical data within a date range #[ink(message)] diff --git a/contracts/traits/src/property.rs b/contracts/traits/src/property.rs index b8f0c82e..7f3e589a 100644 --- a/contracts/traits/src/property.rs +++ b/contracts/traits/src/property.rs @@ -3,10 +3,10 @@ //! This module contains the core property-related types, metadata structures, //! and trait definitions for property registration, escrow, and management. +use crate::compliance::Jurisdiction; use ink::prelude::string::String; use ink::prelude::vec::Vec; use ink::primitives::AccountId; -use crate::compliance::Jurisdiction; // ========================================================================= // Data Types diff --git a/indexer/src/main.rs b/indexer/src/main.rs index 8384fc71..099a3530 100644 --- a/indexer/src/main.rs +++ b/indexer/src/main.rs @@ -105,7 +105,9 @@ async fn main() -> anyhow::Result<()> { .route("/contracts", get(crate::api::list_contracts)) .route("/version", get(crate::api::api_version)) .with_state(api_state.clone()) - .layer(axum::middleware::from_fn(crate::api::set_api_version_header)); + .layer(axum::middleware::from_fn( + crate::api::set_api_version_header, + )); let rest_router = Router::new() .route("/health", get(health)) diff --git a/tests/bridge_chaos.rs b/tests/bridge_chaos.rs index 7fee9487..e2037bad 100644 --- a/tests/bridge_chaos.rs +++ b/tests/bridge_chaos.rs @@ -13,11 +13,11 @@ #[cfg(test)] mod bridge_chaos { use bridge::bridge::{ - BridgeOperationStatus, BridgeOperation, ChainTxStatus, Error, PauseFlags, PauseReason, + BridgeOperation, BridgeOperationStatus, ChainTxStatus, Error, PauseFlags, PauseReason, PropertyBridge, }; - use propchain_traits::PropertyMetadata; use ink::env::{test, DefaultEnvironment}; + use propchain_traits::PropertyMetadata; // ── Helpers ─────────────────────────────────────────────────────────────── @@ -58,7 +58,9 @@ mod bridge_chaos { test::set_caller::(alice()); bridge.add_validator(alice()).expect("add validator alice"); bridge.add_validator(bob()).expect("add validator bob"); - bridge.add_validator(charlie()).expect("add validator charlie"); + bridge + .add_validator(charlie()) + .expect("add validator charlie"); bridge } @@ -162,9 +164,8 @@ mod bridge_chaos { // Attempt 3: try to register a trade with a very large amount_in that might // trip the hourly volume limit. The bridge must either accept or return a // domain error — it must not panic. - let result_register = bridge.register_cross_chain_trade( - 1, None, 2, bob(), u128::MAX / 4, 0, - ); + let result_register = + bridge.register_cross_chain_trade(1, None, 2, bob(), u128::MAX / 4, 0); // Either Ok or a recognised error — never a panic match result_register { Ok(_) | Err(Error::RateLimitExceeded) | Err(Error::OperationPaused) => {} @@ -293,14 +294,8 @@ mod bridge_chaos { // Operations must work normally after chaos test::set_caller::(alice()); - let result = bridge.initiate_bridge_multisig( - 1, - 2, - bob(), - 2, - Some(100), - make_metadata("post-chaos"), - ); + let result = + bridge.initiate_bridge_multisig(1, 2, bob(), 2, Some(100), make_metadata("post-chaos")); assert!( result.is_ok(), "bridge should accept new requests after all pauses are cleared" @@ -458,7 +453,10 @@ mod bridge_chaos { // Analytics: 3 requests, 1 transaction completed let analytics = bridge.get_bridge_analytics(); assert_eq!(analytics.total_requests, 3); - assert_eq!(analytics.total_transactions, 1, "only r1 should be a completed transaction"); + assert_eq!( + analytics.total_transactions, 1, + "only r1 should be a completed transaction" + ); } // ── Chaos Test 7: No funds double-spent ────────────────────────────────── @@ -476,9 +474,13 @@ mod bridge_chaos { // Collect 2 valid signatures test::set_caller::(alice()); - bridge.sign_bridge_request(request_id, true).expect("alice sign"); + bridge + .sign_bridge_request(request_id, true) + .expect("alice sign"); test::set_caller::(bob()); - bridge.sign_bridge_request(request_id, true).expect("bob sign"); + bridge + .sign_bridge_request(request_id, true) + .expect("bob sign"); // First execution must succeed test::set_caller::(alice()); @@ -515,13 +517,17 @@ mod bridge_chaos { .expect("pre-pause request"); // Pause the bridge - bridge - .set_emergency_pause(true) - .expect("admin can pause"); + bridge.set_emergency_pause(true).expect("admin can pause"); // New requests must be blocked - let result = bridge - .initiate_bridge_multisig(2, 2, charlie(), 2, Some(200), make_metadata("post-pause")); + let result = bridge.initiate_bridge_multisig( + 2, + 2, + charlie(), + 2, + Some(200), + make_metadata("post-pause"), + ); assert_eq!( result, Err(Error::OperationPaused), @@ -538,9 +544,17 @@ mod bridge_chaos { let _ = bridge.get_bridge_health_status(); // Unpause and verify operations resume - bridge.set_emergency_pause(false).expect("admin can unpause"); - let post_unpause = bridge - .initiate_bridge_multisig(2, 2, charlie(), 2, Some(200), make_metadata("post-unpause")); + bridge + .set_emergency_pause(false) + .expect("admin can unpause"); + let post_unpause = bridge.initiate_bridge_multisig( + 2, + 2, + charlie(), + 2, + Some(200), + make_metadata("post-unpause"), + ); assert!( post_unpause.is_ok(), "new requests must be allowed after unpause" diff --git a/tests/bridge_load_tests.rs b/tests/bridge_load_tests.rs index 3e46413a..d32e52a1 100644 --- a/tests/bridge_load_tests.rs +++ b/tests/bridge_load_tests.rs @@ -43,7 +43,6 @@ mod bridge_extreme_load_tests { daily_counts: Mutex>, rate_limit: u64, paused: std::sync::atomic::AtomicBool, - } impl MockBridge { @@ -88,7 +87,7 @@ mod bridge_extreme_load_tests { status: BridgeStatus::Pending, expires_at_ms: expires_at, }); - + Some(id) } diff --git a/tests/integration_governance_staking.rs b/tests/integration_governance_staking.rs index a4fb437f..8e49d927 100644 --- a/tests/integration_governance_staking.rs +++ b/tests/integration_governance_staking.rs @@ -19,7 +19,7 @@ mod integration_governance_staking { // ── Governance contract ─────────────────────────────────────────────────── use governance::governance::{ - Error as GovernanceError, GovernanceAction, GovernanceProposal, Governance, + Error as GovernanceError, Governance, GovernanceAction, GovernanceProposal, }; use ink::env::{test, DefaultEnvironment}; @@ -65,7 +65,9 @@ mod integration_governance_staking { test::set_caller::(alice()); let stake_amount: u128 = 10_000; - staking.stake(stake_amount, LockPeriod::Flexible).expect("stake should succeed"); + staking + .stake(stake_amount, LockPeriod::Flexible) + .expect("stake should succeed"); // Governance power must equal staked amount immediately after staking let power = staking.get_governance_power(alice()); @@ -88,7 +90,9 @@ mod integration_governance_staking { // Alice stakes test::set_caller::(alice()); - staking.stake(stake_amount, LockPeriod::Flexible).expect("alice stake"); + staking + .stake(stake_amount, LockPeriod::Flexible) + .expect("alice stake"); // Alice delegates governance to bob staking @@ -188,12 +192,18 @@ mod integration_governance_staking { // Alice stakes and delegates to bob test::set_caller::(alice()); - staking.stake(alice_stake, LockPeriod::Flexible).expect("alice stake"); - staking.delegate_governance(bob()).expect("alice delegates to bob"); + staking + .stake(alice_stake, LockPeriod::Flexible) + .expect("alice stake"); + staking + .delegate_governance(bob()) + .expect("alice delegates to bob"); // Bob stakes (creating his own power) test::set_caller::(bob()); - staking.stake(bob_stake, LockPeriod::Flexible).expect("bob stake"); + staking + .stake(bob_stake, LockPeriod::Flexible) + .expect("bob stake"); // Bob's governance power = his own stake + alice's delegated power let bob_power = staking.get_governance_power(bob()); @@ -228,13 +238,19 @@ mod integration_governance_staking { // All three stake test::set_caller::(alice()); - staking.stake(alice_stake, LockPeriod::Flexible).expect("alice stake"); + staking + .stake(alice_stake, LockPeriod::Flexible) + .expect("alice stake"); test::set_caller::(bob()); - staking.stake(bob_stake, LockPeriod::Flexible).expect("bob stake"); + staking + .stake(bob_stake, LockPeriod::Flexible) + .expect("bob stake"); test::set_caller::(charlie()); - staking.stake(charlie_stake, LockPeriod::Flexible).expect("charlie stake"); + staking + .stake(charlie_stake, LockPeriod::Flexible) + .expect("charlie stake"); // Alice creates a proposal test::set_caller::(alice()); @@ -269,8 +285,7 @@ mod integration_governance_staking { "votes_for should equal alice + bob stake" ); assert_eq!( - proposal.votes_against, - charlie_stake, + proposal.votes_against, charlie_stake, "votes_against should equal charlie's stake" ); } @@ -322,11 +337,8 @@ mod integration_governance_staking { // Django is not a signer test::set_caller::(django()); - let result = gov.create_proposal( - Hash::from([2u8; 32]), - GovernanceAction::SaleApproval, - None, - ); + let result = + gov.create_proposal(Hash::from([2u8; 32]), GovernanceAction::SaleApproval, None); assert_eq!( result, Err(GovernanceError::NotASigner), @@ -346,15 +358,21 @@ mod integration_governance_staking { let stake_amount: u128 = 10_000; test::set_caller::(alice()); - staking.stake(stake_amount, LockPeriod::Flexible).expect("alice stake"); + staking + .stake(stake_amount, LockPeriod::Flexible) + .expect("alice stake"); // First delegation: alice → bob - staking.delegate_governance(bob()).expect("first delegation"); + staking + .delegate_governance(bob()) + .expect("first delegation"); assert_eq!(staking.get_governance_power(bob()), stake_amount); assert_eq!(staking.get_governance_power(alice()), 0); // Re-delegation: alice → charlie - staking.delegate_governance(charlie()).expect("second delegation"); + staking + .delegate_governance(charlie()) + .expect("second delegation"); assert_eq!( staking.get_governance_power(charlie()), stake_amount, diff --git a/tests/regression/closed_bugs.rs b/tests/regression/closed_bugs.rs index 75a337a9..90e77560 100644 --- a/tests/regression/closed_bugs.rs +++ b/tests/regression/closed_bugs.rs @@ -140,7 +140,7 @@ mod closed_bugs_regression { #[ink::test] fn governance_threshold_enforced_before_execution() { use governance::governance::{ - Error as GovError, GovernanceAction, GovernanceProposal, Governance, + Error as GovError, Governance, GovernanceAction, GovernanceProposal, }; use ink::primitives::Hash; @@ -148,7 +148,11 @@ mod closed_bugs_regression { let mut gov = Governance::new(vec![alice(), bob()], 2, 0); let pid = gov - .create_proposal(Hash::from([1u8; 32]), GovernanceAction::ModifyProperty, None) + .create_proposal( + Hash::from([1u8; 32]), + GovernanceAction::ModifyProperty, + None, + ) .expect("create proposal"); // Only one vote (alice) — threshold is 2 diff --git a/tests/regression/mod.rs b/tests/regression/mod.rs index c4780118..f40395b1 100644 --- a/tests/regression/mod.rs +++ b/tests/regression/mod.rs @@ -7,7 +7,6 @@ /// - `reinsurance_stats_derives` — encodes/decodes ReinsuranceStats (fixed in earlier work) /// - `reentrancy_guard` — verifies the guard on each protected function /// - `closed_bugs` — miscellaneous one-off regressions - pub mod closed_bugs; pub mod reentrancy_guard; pub mod reinsurance_stats_derives; diff --git a/tests/regression/reinsurance_stats_derives.rs b/tests/regression/reinsurance_stats_derives.rs index 8b7bbf6b..2db79c23 100644 --- a/tests/regression/reinsurance_stats_derives.rs +++ b/tests/regression/reinsurance_stats_derives.rs @@ -90,8 +90,7 @@ mod reinsurance_stats_derives { net_recovery: 0, }; let encoded = zero.encode(); - let decoded = - ReinsuranceStats::decode(&mut encoded.as_slice()).expect("zero-value decode"); + let decoded = ReinsuranceStats::decode(&mut encoded.as_slice()).expect("zero-value decode"); assert_eq!(decoded.agreement_id, 0); assert_eq!(decoded.net_recovery, 0); }