Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 35 additions & 18 deletions contracts/analytics/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<PortfolioPosition>) {
pub fn set_portfolio_positions(
&mut self,
owner: AccountId,
positions: Vec<PortfolioPosition>,
) {
self.ensure_admin();
self.portfolio_positions.insert(owner, &positions);
}
Expand Down Expand Up @@ -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<RebalancingSuggestion> {
pub fn get_rebalancing_suggestions(&self, owner: AccountId) -> Vec<RebalancingSuggestion> {
let positions = self.get_portfolio_positions(owner);
let total_value: u128 = positions.iter().map(|p| p.value).sum();
if total_value == 0 {
Expand Down Expand Up @@ -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.")
};
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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<propchain_traits::KeyRotationRequest> {
pub fn get_pending_admin_rotation(&self) -> Option<propchain_traits::KeyRotationRequest> {
self.pending_admin_rotation.clone()
}
}
Expand Down Expand Up @@ -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]
Expand Down Expand Up @@ -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]
Expand Down Expand Up @@ -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(
Expand All @@ -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
);
}
}
}
Expand Down
27 changes: 17 additions & 10 deletions contracts/crowdfunding/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand All @@ -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;
Expand Down Expand Up @@ -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 {
Expand Down
Loading