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
10 changes: 10 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ members = [
"contracts/analytics",
"contracts/fees",
"contracts/compliance_registry",
"contracts/fractional",
]
resolver = "2"

Expand Down
2 changes: 2 additions & 0 deletions contracts/analytics/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ mod propchain_analytics {
/// Portfolio performance for an individual owner.
#[derive(Debug, Clone, PartialEq, scale::Encode, scale::Decode, ink::storage::traits::StorageLayout)]
#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))]
#[allow(dead_code)]
pub struct PortfolioPerformance {
pub total_value: u128,
pub property_count: u64,
Expand All @@ -40,6 +41,7 @@ mod propchain_analytics {
/// User behavior analytics for a specific account.
#[derive(Debug, Clone, PartialEq, scale::Encode, scale::Decode, ink::storage::traits::StorageLayout)]
#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))]
#[allow(dead_code)]
pub struct UserBehavior {
pub account: AccountId,
pub total_interactions: u64,
Expand Down
28 changes: 28 additions & 0 deletions contracts/fractional/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
[package]
name = "fractional"
version = "0.1.0"
edition = "2021"
authors = ["PropChain Team <dev@propchain.io>"]
license = "MIT"
publish = false

[lib]
name = "fractional"
path = "src/lib.rs"
crate-type = ["cdylib", "rlib"]

[dependencies]
ink = { version = "5.0.0", default-features = false }
scale = { package = "parity-scale-codec", version = "3.6.9", default-features = false, features = ["derive"] }
scale-info = { version = "2.10.0", default-features = false, features = ["derive"] }
propchain_traits = { package = "propchain-traits", path = "../traits", default-features = false }

[features]
default = ["std"]
std = [
"ink/std",
"scale/std",
"scale-info/std",
"propchain_traits/std",
]
ink-as-dependency = []
120 changes: 120 additions & 0 deletions contracts/fractional/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
#![cfg_attr(not(feature = "std"), no_std, no_main)]

#[ink::contract]
mod fractional {
use ink::prelude::vec::Vec;
use ink::storage::Mapping;

#[derive(
Debug,
Clone,
PartialEq,
Eq,
scale::Encode,
scale::Decode,
ink::storage::traits::StorageLayout,
)]
#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))]
pub struct PortfolioItem {
pub token_id: u64,
pub shares: u128,
pub price_per_share: u128,
}

#[derive(
Debug,
Clone,
PartialEq,
Eq,
scale::Encode,
scale::Decode,
ink::storage::traits::StorageLayout,
)]
#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))]
pub struct PortfolioAggregation {
pub total_value: u128,
pub positions: Vec<(u64, u128, u128)>,
}

#[derive(
Debug,
Clone,
PartialEq,
Eq,
scale::Encode,
scale::Decode,
ink::storage::traits::StorageLayout,
)]
#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))]
pub struct TaxReport {
pub total_dividends: u128,
pub total_proceeds: u128,
pub transactions: u64,
}

#[ink(storage)]
pub struct Fractional {
last_prices: Mapping<u64, u128>,
}

impl Fractional {
#[ink(constructor)]
pub fn new() -> Self {
Self {
last_prices: Mapping::default(),
}
}

#[ink(message)]
pub fn set_last_price(&mut self, token_id: u64, price_per_share: u128) {
self.last_prices.insert(token_id, &price_per_share);
}

#[ink(message)]
pub fn get_last_price(&self, token_id: u64) -> Option<u128> {
self.last_prices.get(token_id)
}

#[ink(message)]
pub fn aggregate_portfolio(&self, items: Vec<PortfolioItem>) -> PortfolioAggregation {
let mut total: u128 = 0;
let mut positions: Vec<(u64, u128, u128)> = Vec::new();
for it in items.iter() {
let price = if it.price_per_share > 0 {
it.price_per_share
} else {
self.last_prices.get(it.token_id).unwrap_or(0)
};
let value = price.saturating_mul(it.shares);
total = total.saturating_add(value);
positions.push((it.token_id, it.shares, price));
}
PortfolioAggregation {
total_value: total,
positions,
}
}

#[ink(message)]
pub fn summarize_tax(
&self,
dividends: Vec<(u64, u128)>,
proceeds: Vec<(u64, u128)>,
) -> TaxReport {
let mut total_dividends: u128 = 0;
for d in dividends.iter() {
total_dividends = total_dividends.saturating_add(d.1);
}
let mut total_proceeds: u128 = 0;
for p in proceeds.iter() {
total_proceeds = total_proceeds.saturating_add(p.1);
}
TaxReport {
total_dividends,
total_proceeds,
transactions: (dividends.len() + proceeds.len()) as u64,
}
}
}
}

51 changes: 51 additions & 0 deletions contracts/lib/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ mod propchain_contracts {
oracle: Option<AccountId>,
/// Fee manager contract for dynamic fees and market mechanism (optional)
fee_manager: Option<AccountId>,
/// Fractional properties info
fractional: Mapping<u64, FractionalInfo>,
}

/// Escrow information
Expand Down Expand Up @@ -145,6 +147,16 @@ mod propchain_contracts {
pub registered_at: u64,
}

#[derive(
Debug, Clone, PartialEq, Eq, scale::Encode, scale::Decode, ink::storage::traits::StorageLayout,
)]
#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))]
pub struct FractionalInfo {
pub total_shares: u128,
pub enabled: bool,
pub created_at: u64,
}

/// Global analytics data
#[derive(
Debug, Clone, PartialEq, scale::Encode, scale::Decode, ink::storage::traits::StorageLayout,
Expand Down Expand Up @@ -775,6 +787,7 @@ mod propchain_contracts {
pause_guardians: Mapping::default(),
oracle: None,
fee_manager: None,
fractional: Mapping::default(),
};

// Emit contract initialization event
Expand Down Expand Up @@ -2519,6 +2532,44 @@ mod propchain_contracts {
self.refund_escrow(escrow_id)
}
}

impl PropertyRegistry {
#[ink(message)]
pub fn enable_fractional(
&mut self,
property_id: u64,
total_shares: u128,
) -> Result<(), Error> {
let caller = self.env().caller();
let property = self.properties.get(property_id).ok_or(Error::PropertyNotFound)?;
if caller != self.admin && caller != property.owner {
return Err(Error::Unauthorized);
}
if total_shares == 0 {
return Err(Error::InvalidMetadata);
}
let info = FractionalInfo {
total_shares,
enabled: true,
created_at: self.env().block_timestamp(),
};
self.fractional.insert(property_id, &info);
Ok(())
}

#[ink(message)]
pub fn get_fractional_info(&self, property_id: u64) -> Option<FractionalInfo> {
self.fractional.get(property_id)
}

#[ink(message)]
pub fn is_fractional(&self, property_id: u64) -> bool {
self.fractional
.get(property_id)
.map(|i: FractionalInfo| i.enabled)
.unwrap_or(false)
}
}
}

#[cfg(test)]
Expand Down
2 changes: 1 addition & 1 deletion contracts/oracle/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -523,7 +523,7 @@ mod propchain_oracle {
}
OracleSourceType::AIModel => {
// AI model integration - call AI valuation contract
if let Some(ai_contract) = self.ai_valuation_contract {
if let Some(_ai_contract) = self.ai_valuation_contract {
// In production, this would make a cross-contract call to AI valuation engine
// For now, return a mock price based on property_id
let mock_price = 500000u128 + (property_id as u128 * 1000);
Expand Down
Loading
Loading