diff --git a/soroban/contracts/energy-project-funding-escrow/src/escrow.rs b/soroban/contracts/energy-project-funding-escrow/src/escrow.rs index 416cf05..6d2ae53 100644 --- a/soroban/contracts/energy-project-funding-escrow/src/escrow.rs +++ b/soroban/contracts/energy-project-funding-escrow/src/escrow.rs @@ -1,6 +1,6 @@ -use soroban_sdk::{Address, Env, String, Symbol, Vec}; use crate::types::*; -use crate::utils::{validate_project_setup, validate_funding_amount}; +use crate::utils::{validate_funding_amount, validate_project_setup}; +use soroban_sdk::{Address, Env, String, Symbol, Vec}; pub fn initialize_project( env: Env, @@ -15,11 +15,18 @@ pub fn initialize_project( ) -> u64 { investor.require_auth(); - if !validate_project_setup(&env, &investor, &project_manager, total_funding, milestone_count) { + if !validate_project_setup( + &env, + &investor, + &project_manager, + total_funding, + milestone_count, + ) { panic!("Invalid project setup parameters"); } - let project_id: u64 = env.storage() + let project_id: u64 = env + .storage() .instance() .get(&DataKey::NextProjectId) .unwrap_or(1); @@ -64,7 +71,8 @@ pub fn deposit_funds(env: Env, project_id: u64, depositor: Address, amount: i128 panic!("Invalid funding amount"); } - let mut project: ProjectDetails = env.storage() + let mut project: ProjectDetails = env + .storage() .persistent() .get(&DataKey::Project(project_id)) .unwrap_or_else(|| panic!("Project not found")); @@ -92,7 +100,8 @@ pub fn deposit_funds(env: Env, project_id: u64, depositor: Address, amount: i128 pub fn release_funds(env: Env, project_id: u64, milestone_id: u32, approver: Address) { approver.require_auth(); - let mut project: ProjectDetails = env.storage() + let mut project: ProjectDetails = env + .storage() .persistent() .get(&DataKey::Project(project_id)) .unwrap_or_else(|| panic!("Project not found")); @@ -101,7 +110,8 @@ pub fn release_funds(env: Env, project_id: u64, milestone_id: u32, approver: Add panic!("Project is not active"); } - let milestone: MilestoneDetails = env.storage() + let milestone: MilestoneDetails = env + .storage() .persistent() .get(&DataKey::Milestone(project_id, milestone_id)) .unwrap_or_else(|| panic!("Milestone not found")); @@ -110,7 +120,8 @@ pub fn release_funds(env: Env, project_id: u64, milestone_id: u32, approver: Add panic!("Milestone not completed"); } - let mut pending_release: PendingRelease = env.storage() + let mut pending_release: PendingRelease = env + .storage() .persistent() .get(&DataKey::PendingRelease(project_id)) .unwrap_or_else(|| panic!("No pending release found")); @@ -119,7 +130,8 @@ pub fn release_funds(env: Env, project_id: u64, milestone_id: u32, approver: Add panic!("Milestone mismatch"); } - let multisig_config: Option = env.storage() + let multisig_config: Option = env + .storage() .persistent() .get(&DataKey::MultisigRequirement(project_id)); @@ -141,7 +153,12 @@ pub fn release_funds(env: Env, project_id: u64, milestone_id: u32, approver: Add env.events().publish( (Symbol::new(&env, "ApprovalAdded"),), - (project_id, milestone_id, approver, pending_release.current_approvals), + ( + project_id, + milestone_id, + approver, + pending_release.current_approvals, + ), ); return; } @@ -177,14 +194,20 @@ pub fn release_funds(env: Env, project_id: u64, milestone_id: u32, approver: Add env.events().publish( (Symbol::new(&env, "FundsReleased"),), - (project_id, milestone_id, project.project_manager, release_amount), + ( + project_id, + milestone_id, + project.project_manager, + release_amount, + ), ); } pub fn request_refund(env: Env, project_id: u64, requester: Address, amount: i128, reason: String) { requester.require_auth(); - let project: ProjectDetails = env.storage() + let project: ProjectDetails = env + .storage() .persistent() .get(&DataKey::Project(project_id)) .unwrap_or_else(|| panic!("Project not found")); @@ -225,12 +248,14 @@ pub fn request_refund(env: Env, project_id: u64, requester: Address, amount: i12 pub fn process_refund(env: Env, project_id: u64, approver: Address) { approver.require_auth(); - let mut project: ProjectDetails = env.storage() + let mut project: ProjectDetails = env + .storage() .persistent() .get(&DataKey::Project(project_id)) .unwrap_or_else(|| panic!("Project not found")); - let mut refund_request: RefundRequest = env.storage() + let mut refund_request: RefundRequest = env + .storage() .persistent() .get(&DataKey::RefundRequest(project_id)) .unwrap_or_else(|| panic!("No refund request found")); @@ -271,7 +296,8 @@ pub fn get_project_details(env: Env, project_id: u64) -> ProjectDetails { } pub fn get_available_funds(env: Env, project_id: u64) -> i128 { - let project: ProjectDetails = env.storage() + let project: ProjectDetails = env + .storage() .persistent() .get(&DataKey::Project(project_id)) .unwrap_or_else(|| panic!("Project not found")); @@ -289,7 +315,8 @@ pub fn setup_multisig( ) { setup_by.require_auth(); - let project: ProjectDetails = env.storage() + let project: ProjectDetails = env + .storage() .persistent() .get(&DataKey::Project(project_id)) .unwrap_or_else(|| panic!("Project not found")); @@ -317,4 +344,4 @@ pub fn setup_multisig( (Symbol::new(&env, "MultisigConfigured"),), (project_id, required_signatures, threshold_amount), ); -} \ No newline at end of file +} diff --git a/soroban/contracts/energy-project-funding-escrow/src/lib.rs b/soroban/contracts/energy-project-funding-escrow/src/lib.rs index 68aab45..5dad9a1 100644 --- a/soroban/contracts/energy-project-funding-escrow/src/lib.rs +++ b/soroban/contracts/energy-project-funding-escrow/src/lib.rs @@ -7,7 +7,7 @@ mod types; mod utils; #[cfg(test)] -mod test; +mod tests; pub use escrow::*; pub use milestones::*; @@ -224,8 +224,13 @@ impl EnergyProjectFundingEscrow { energy_type: String, expected_capacity: i128, ) -> bool { - utils::validate_project_setup(&env, &investor, &project_manager, total_funding, milestone_count) && - utils::validate_energy_project_data(&env, &energy_type, expected_capacity) + utils::validate_project_setup( + &env, + &investor, + &project_manager, + total_funding, + milestone_count, + ) && utils::validate_energy_project_data(&env, &energy_type, expected_capacity) } /// Calculate milestone funding amount based on percentage @@ -266,4 +271,4 @@ impl EnergyProjectFundingEscrow { let milestone = milestones::get_milestone_details(env.clone(), project_id, milestone_id); utils::format_milestone_status_message(&milestone.status) } -} \ No newline at end of file +} diff --git a/soroban/contracts/energy-project-funding-escrow/src/milestones.rs b/soroban/contracts/energy-project-funding-escrow/src/milestones.rs index 0bdaed4..b164e60 100644 --- a/soroban/contracts/energy-project-funding-escrow/src/milestones.rs +++ b/soroban/contracts/energy-project-funding-escrow/src/milestones.rs @@ -1,6 +1,6 @@ -use soroban_sdk::{Address, Env, String, Symbol, Vec}; use crate::types::*; -use crate::utils::{validate_milestone_data, calculate_milestone_funding}; +use crate::utils::{calculate_milestone_funding, validate_milestone_data}; +use soroban_sdk::{Address, Env, String, Symbol, Vec}; pub fn create_milestone( env: Env, @@ -16,7 +16,8 @@ pub fn create_milestone( ) -> u32 { creator.require_auth(); - let project: ProjectDetails = env.storage() + let project: ProjectDetails = env + .storage() .persistent() .get(&DataKey::Project(project_id)) .unwrap_or_else(|| panic!("Project not found")); @@ -34,7 +35,8 @@ pub fn create_milestone( } let milestone_id = get_next_milestone_id(&env, project_id); - let funding_amount = calculate_milestone_funding(&env, project.total_funding, funding_percentage); + let funding_amount = + calculate_milestone_funding(&env, project.total_funding, funding_percentage); let milestone = MilestoneDetails { project_id, @@ -67,7 +69,8 @@ pub fn create_milestone( pub fn start_milestone(env: Env, project_id: u64, milestone_id: u32, starter: Address) { starter.require_auth(); - let project: ProjectDetails = env.storage() + let project: ProjectDetails = env + .storage() .persistent() .get(&DataKey::Project(project_id)) .unwrap_or_else(|| panic!("Project not found")); @@ -76,7 +79,8 @@ pub fn start_milestone(env: Env, project_id: u64, milestone_id: u32, starter: Ad panic!("Only project manager can start milestones"); } - let mut milestone: MilestoneDetails = env.storage() + let mut milestone: MilestoneDetails = env + .storage() .persistent() .get(&DataKey::Milestone(project_id, milestone_id)) .unwrap_or_else(|| panic!("Milestone not found")); @@ -107,7 +111,8 @@ pub fn verify_milestone( ) { verifier.require_auth(); - let project: ProjectDetails = env.storage() + let project: ProjectDetails = env + .storage() .persistent() .get(&DataKey::Project(project_id)) .unwrap_or_else(|| panic!("Project not found")); @@ -116,7 +121,8 @@ pub fn verify_milestone( panic!("Unauthorized to verify milestone"); } - let mut milestone: MilestoneDetails = env.storage() + let mut milestone: MilestoneDetails = env + .storage() .persistent() .get(&DataKey::Milestone(project_id, milestone_id)) .unwrap_or_else(|| panic!("Milestone not found")); @@ -125,15 +131,23 @@ pub fn verify_milestone( panic!("Milestone is not in progress"); } - if !milestone.required_verifications.contains(&verification_type) { + if !milestone + .required_verifications + .contains(&verification_type) + { panic!("Verification type not required"); } - if milestone.completed_verifications.contains(&verification_type) { + if milestone + .completed_verifications + .contains(&verification_type) + { panic!("Verification already completed"); } - milestone.completed_verifications.push_back(verification_type.clone()); + milestone + .completed_verifications + .push_back(verification_type.clone()); if milestone.completed_verifications.len() == milestone.required_verifications.len() { milestone.status = MilestoneStatus::Completed; @@ -179,7 +193,8 @@ pub fn fail_milestone( ) { manager.require_auth(); - let project: ProjectDetails = env.storage() + let project: ProjectDetails = env + .storage() .persistent() .get(&DataKey::Project(project_id)) .unwrap_or_else(|| panic!("Project not found")); @@ -188,7 +203,8 @@ pub fn fail_milestone( panic!("Only project manager can fail milestones"); } - let mut milestone: MilestoneDetails = env.storage() + let mut milestone: MilestoneDetails = env + .storage() .persistent() .get(&DataKey::Milestone(project_id, milestone_id)) .unwrap_or_else(|| panic!("Milestone not found")); @@ -219,7 +235,8 @@ pub fn update_project_metrics( ) { updater.require_auth(); - let project: ProjectDetails = env.storage() + let project: ProjectDetails = env + .storage() .persistent() .get(&DataKey::Project(project_id)) .unwrap_or_else(|| panic!("Project not found")); @@ -245,7 +262,12 @@ pub fn update_project_metrics( env.events().publish( (Symbol::new(&env, "MetricsUpdated"),), - (project_id, actual_energy_output, actual_carbon_offset, efficiency_rating), + ( + project_id, + actual_energy_output, + actual_carbon_offset, + efficiency_rating, + ), ); } @@ -257,7 +279,8 @@ pub fn get_milestone_details(env: Env, project_id: u64, milestone_id: u32) -> Mi } pub fn get_project_milestones(env: Env, project_id: u64) -> Vec { - let project: ProjectDetails = env.storage() + let project: ProjectDetails = env + .storage() .persistent() .get(&DataKey::Project(project_id)) .unwrap_or_else(|| panic!("Project not found")); @@ -265,9 +288,11 @@ pub fn get_project_milestones(env: Env, project_id: u64) -> Vec(&DataKey::Milestone(project_id, i)) { + .get::(&DataKey::Milestone(project_id, i)) + { milestones.push_back(milestone); } } @@ -282,7 +307,8 @@ pub fn get_pending_release(env: Env, project_id: u64) -> Option } pub fn calculate_project_progress(env: Env, project_id: u64) -> u32 { - let project: ProjectDetails = env.storage() + let project: ProjectDetails = env + .storage() .persistent() .get(&DataKey::Project(project_id)) .unwrap_or_else(|| panic!("Project not found")); @@ -290,9 +316,11 @@ pub fn calculate_project_progress(env: Env, project_id: u64) -> u32 { let mut completed_milestones = 0u32; for i in 1..=project.milestone_count { - if let Some(milestone) = env.storage() + if let Some(milestone) = env + .storage() .persistent() - .get::(&DataKey::Milestone(project_id, i)) { + .get::(&DataKey::Milestone(project_id, i)) + { if milestone.status == MilestoneStatus::Completed { completed_milestones += 1; } @@ -307,7 +335,8 @@ pub fn calculate_project_progress(env: Env, project_id: u64) -> u32 { } fn get_next_milestone_id(env: &Env, project_id: u64) -> u32 { - let project: ProjectDetails = env.storage() + let project: ProjectDetails = env + .storage() .persistent() .get(&DataKey::Project(project_id)) .unwrap_or_else(|| panic!("Project not found")); @@ -315,14 +344,16 @@ fn get_next_milestone_id(env: &Env, project_id: u64) -> u32 { let mut next_id = 1u32; for i in 1..=project.milestone_count + 10 { - if env.storage() + if env + .storage() .persistent() .get::(&DataKey::Milestone(project_id, i)) - .is_none() { + .is_none() + { next_id = i; break; } } next_id -} \ No newline at end of file +} diff --git a/soroban/contracts/energy-project-funding-escrow/src/test.rs b/soroban/contracts/energy-project-funding-escrow/src/test.rs deleted file mode 100644 index 51d3743..0000000 --- a/soroban/contracts/energy-project-funding-escrow/src/test.rs +++ /dev/null @@ -1,326 +0,0 @@ -#![cfg(test)] - -use super::*; -use soroban_sdk::{testutils::Address as _, Address, Env, String, Vec}; - -#[test] -fn test_contract_initialization() { - let env = Env::default(); - let contract_id = env.register(EnergyProjectFundingEscrow, ()); - let client = EnergyProjectFundingEscrowClient::new(&env, &contract_id); - - client.initialize(); - - // Test that next project ID is set to 1 - let next_id: u64 = env.as_contract(&contract_id, || { - env.storage().instance().get(&DataKey::NextProjectId).unwrap() - }); - assert_eq!(next_id, 1); -} - -#[test] -fn test_project_initialization() { - let env = Env::default(); - let contract_id = env.register(EnergyProjectFundingEscrow, ()); - let client = EnergyProjectFundingEscrowClient::new(&env, &contract_id); - - client.initialize(); - - let investor = Address::generate(&env); - let project_manager = Address::generate(&env); - let name = String::from_str(&env, "Solar Farm Alpha"); - let description = String::from_str(&env, "100MW solar installation"); - let total_funding = 500_000_000_000i128; // 500K XLM (within limits) - let milestone_count = 5u32; - let energy_type = String::from_str(&env, "solar"); - let expected_capacity = 100_000_000i128; // 100MW - - env.mock_all_auths(); - - let project_id = client.initialize_project( - &investor, - &project_manager, - &name, - &description, - &total_funding, - &milestone_count, - &energy_type, - &expected_capacity, - ); - - assert_eq!(project_id, 1); - - let project = client.get_project(&project_id); - assert_eq!(project.investor, investor); - assert_eq!(project.project_manager, project_manager); - assert_eq!(project.total_funding, total_funding); - assert_eq!(project.milestone_count, milestone_count); - assert_eq!(project.status, ProjectStatus::Active); -} - -#[test] -fn test_milestone_creation() { - let env = Env::default(); - let contract_id = env.register(EnergyProjectFundingEscrow, ()); - let client = EnergyProjectFundingEscrowClient::new(&env, &contract_id); - - client.initialize(); - - let investor = Address::generate(&env); - let project_manager = Address::generate(&env); - env.mock_all_auths(); - - let project_id = client.initialize_project( - &investor, - &project_manager, - &String::from_str(&env, "Test Project"), - &String::from_str(&env, "Test Description"), - &10_000_000_000i128, - &3u32, - &String::from_str(&env, "solar"), - &10_000_000i128, - ); - - let milestone_name = String::from_str(&env, "Site Preparation"); - let milestone_desc = String::from_str(&env, "Prepare construction site"); - let funding_percentage = 30u32; - let due_date = env.ledger().timestamp() + 86400 * 30; // 30 days from now - - let mut required_verifications = Vec::new(&env); - required_verifications.push_back(String::from_str(&env, "environmental_impact")); - required_verifications.push_back(String::from_str(&env, "safety_compliance")); - - let milestone_id = client.create_milestone( - &project_id, - &project_manager, - &milestone_name, - &milestone_desc, - &funding_percentage, - &required_verifications, - &due_date, - &Some(3_000_000i128), - &Some(1_000i128), - ); - - assert_eq!(milestone_id, 1); - - let milestone = client.get_milestone(&project_id, &milestone_id); - assert_eq!(milestone.funding_percentage, funding_percentage); - assert_eq!(milestone.status, MilestoneStatus::Pending); -} - -#[test] -fn test_milestone_verification_and_completion() { - let env = Env::default(); - let contract_id = env.register(EnergyProjectFundingEscrow, ()); - let client = EnergyProjectFundingEscrowClient::new(&env, &contract_id); - - client.initialize(); - - let investor = Address::generate(&env); - let project_manager = Address::generate(&env); - env.mock_all_auths(); - - let project_id = client.initialize_project( - &investor, - &project_manager, - &String::from_str(&env, "Test Project"), - &String::from_str(&env, "Test Description"), - &10_000_000_000i128, - &2u32, - &String::from_str(&env, "wind"), - &20_000_000i128, - ); - - let mut required_verifications = Vec::new(&env); - required_verifications.push_back(String::from_str(&env, "technical_specification")); - - let milestone_id = client.create_milestone( - &project_id, - &project_manager, - &String::from_str(&env, "First Milestone"), - &String::from_str(&env, "Complete initial phase"), - &50u32, - &required_verifications, - &(env.ledger().timestamp() + 86400 * 60), - &None, - &None, - ); - - // Start the milestone - client.start_milestone(&project_id, &milestone_id, &project_manager); - - let milestone = client.get_milestone(&project_id, &milestone_id); - assert_eq!(milestone.status, MilestoneStatus::InProgress); - - // Verify the milestone - client.verify_milestone( - &project_id, - &milestone_id, - &project_manager, - &String::from_str(&env, "technical_specification"), - &String::from_str(&env, "Specification approved"), - ); - - let completed_milestone = client.get_milestone(&project_id, &milestone_id); - assert_eq!(completed_milestone.status, MilestoneStatus::Completed); - - // Check pending release - let pending_release = client.get_pending_release(&project_id); - assert!(pending_release.is_some()); -} - -#[test] -fn test_funds_release() { - let env = Env::default(); - let contract_id = env.register(EnergyProjectFundingEscrow, ()); - let client = EnergyProjectFundingEscrowClient::new(&env, &contract_id); - - client.initialize(); - - let investor = Address::generate(&env); - let project_manager = Address::generate(&env); - let total_funding = 20_000_000_000i128; - - env.mock_all_auths(); - - let project_id = client.initialize_project( - &investor, - &project_manager, - &String::from_str(&env, "Funding Test Project"), - &String::from_str(&env, "Testing fund release"), - &total_funding, - &2u32, - &String::from_str(&env, "hydro"), - &5_000_000i128, - ); - - let mut required_verifications = Vec::new(&env); - required_verifications.push_back(String::from_str(&env, "performance_metrics")); - - let milestone_id = client.create_milestone( - &project_id, - &project_manager, - &String::from_str(&env, "Performance Milestone"), - &String::from_str(&env, "Achieve performance targets"), - &40u32, - &required_verifications, - &(env.ledger().timestamp() + 86400 * 90), - &Some(2_000_000i128), - &Some(500i128), - ); - - client.start_milestone(&project_id, &milestone_id, &project_manager); - - client.verify_milestone( - &project_id, - &milestone_id, - &project_manager, - &String::from_str(&env, "performance_metrics"), - &String::from_str(&env, "Performance targets achieved"), - ); - - let initial_available = client.get_available_funds(&project_id); - assert_eq!(initial_available, total_funding); - - // Release funds - client.release_funds(&project_id, &milestone_id, &investor); - - let project = client.get_project(&project_id); - let expected_released = (total_funding * 40) / 100; // 40% of total funding - assert_eq!(project.released_funding, expected_released); - - let remaining_available = client.get_available_funds(&project_id); - assert_eq!(remaining_available, total_funding - expected_released); -} - -#[test] -fn test_refund_request_and_processing() { - let env = Env::default(); - let contract_id = env.register(EnergyProjectFundingEscrow, ()); - let client = EnergyProjectFundingEscrowClient::new(&env, &contract_id); - - client.initialize(); - - let investor = Address::generate(&env); - let project_manager = Address::generate(&env); - let total_funding = 15_000_000_000i128; - - env.mock_all_auths(); - - let project_id = client.initialize_project( - &investor, - &project_manager, - &String::from_str(&env, "Refund Test Project"), - &String::from_str(&env, "Testing refund process"), - &total_funding, - &3u32, - &String::from_str(&env, "geothermal"), - &8_000_000i128, - ); - - let refund_amount = 5_000_000_000i128; - let reason = String::from_str(&env, "Project delays due to permit issues"); - - client.request_refund(&project_id, &investor, &refund_amount, &reason); - - // Process the refund - client.process_refund(&project_id, &project_manager); - - let project = client.get_project(&project_id); - assert_eq!(project.status, ProjectStatus::Refunded); - assert_eq!(project.escrow_status, EscrowStatus::Refunded); -} - -#[test] -fn test_validation_functions() { - let env = Env::default(); - let contract_id = env.register(EnergyProjectFundingEscrow, ()); - let client = EnergyProjectFundingEscrowClient::new(&env, &contract_id); - - let investor = Address::generate(&env); - let project_manager = Address::generate(&env); - - // Test valid project validation - let is_valid = client.validate_project( - &investor, - &project_manager, - &10_000_000_000i128, - &5u32, - &String::from_str(&env, "solar"), - &50_000_000i128, - ); - assert!(is_valid); - - // Test milestone funding calculation - let total_funding = 100_000_000_000i128; - let percentage = 25u32; - let expected_amount = client.calculate_milestone_funding(&total_funding, &percentage); - assert_eq!(expected_amount, 25_000_000_000i128); -} - -#[test] -#[should_panic(expected = "Invalid project setup parameters")] -fn test_invalid_project_setup() { - let env = Env::default(); - let contract_id = env.register(EnergyProjectFundingEscrow, ()); - let client = EnergyProjectFundingEscrowClient::new(&env, &contract_id); - - client.initialize(); - - let same_address = Address::generate(&env); - - env.mock_all_auths(); - - // Should panic because investor and project manager are the same - client.initialize_project( - &same_address, - &same_address, - &String::from_str(&env, "Invalid Project"), - &String::from_str(&env, "This should fail"), - &10_000_000_000i128, - &3u32, - &String::from_str(&env, "solar"), - &10_000_000i128, - ); -} \ No newline at end of file diff --git a/soroban/contracts/energy-project-funding-escrow/src/tests/escrow.rs b/soroban/contracts/energy-project-funding-escrow/src/tests/escrow.rs new file mode 100644 index 0000000..5af8ac5 --- /dev/null +++ b/soroban/contracts/energy-project-funding-escrow/src/tests/escrow.rs @@ -0,0 +1,215 @@ +#![cfg(test)] + +use super::utils::*; +use crate::*; +use soroban_sdk::String; + +// ============ CONTRACT INITIALIZATION TESTS ============ + +#[test] +fn test_contract_initialization() { + let ctx = setup_test(); + + ctx.client.initialize(); + + // Test that next project ID is set to 1 + let next_id = get_next_project_id(&ctx); + assert_eq!(next_id, 1); +} + +// ============ PROJECT INITIALIZATION TESTS ============ + +#[test] +fn test_project_initialization() { + let ctx = setup_initialized(); + + let investor = create_investor(&ctx.env); + let project_manager = create_project_manager(&ctx.env); + let name = String::from_str(&ctx.env, "Solar Farm Alpha"); + let description = String::from_str(&ctx.env, "100MW solar installation"); + let total_funding = 500_000_000_000i128; // 500K XLM (within limits) + let milestone_count = 5u32; + let energy_type = String::from_str(&ctx.env, "solar"); + let expected_capacity = 100_000_000i128; // 100MW + + ctx.env.mock_all_auths(); + + let project_id = ctx.client.initialize_project( + &investor, + &project_manager, + &name, + &description, + &total_funding, + &milestone_count, + &energy_type, + &expected_capacity, + ); + + assert_eq!(project_id, 1); + + let project = get_project(&ctx, project_id); + assert_eq!(project.investor, investor); + assert_eq!(project.project_manager, project_manager); + assert_eq!(project.total_funding, total_funding); + assert_eq!(project.milestone_count, milestone_count); + assert_eq!(project.status, ProjectStatus::Active); +} + +// ============ VALIDATION TESTS ============ + +#[test] +fn test_validation_functions() { + let ctx = setup_test(); + + let investor = create_investor(&ctx.env); + let project_manager = create_project_manager(&ctx.env); + + // Test valid project validation + let is_valid = ctx.client.validate_project( + &investor, + &project_manager, + &10_000_000_000i128, + &5u32, + &String::from_str(&ctx.env, "solar"), + &50_000_000i128, + ); + assert!(is_valid); + + // Test milestone funding calculation + let total_funding = 100_000_000_000i128; + let percentage = 25u32; + let expected_amount = ctx + .client + .calculate_milestone_funding(&total_funding, &percentage); + assert_eq!(expected_amount, 25_000_000_000i128); +} + +// ============ DEPOSIT TESTS ============ + +#[test] +fn test_deposit_funds() { + let ctx = setup_initialized(); + + let investor = create_investor(&ctx.env); + let project_manager = create_project_manager(&ctx.env); + + let project_id = create_basic_project(&ctx, &investor, &project_manager); + + ctx.env.mock_all_auths(); + + let deposit_amount = 5_000_000_000i128; + ctx.client.deposit(&project_id, &investor, &deposit_amount); + + let project = get_project(&ctx, project_id); + let expected_total = 10_000_000_000i128 + deposit_amount; // original + deposit + assert_eq!(project.total_funding, expected_total); +} + +#[test] +#[should_panic(expected = "Project not found")] +fn test_deposit_non_existent_project() { + let ctx = setup_initialized(); + let investor = create_investor(&ctx.env); + + ctx.env.mock_all_auths(); + + // Try to deposit to non-existent project + ctx.client.deposit(&999u64, &investor, &1_000_000_000i128); +} + +#[test] +fn test_multiple_deposits() { + let ctx = setup_initialized(); + + let investor = create_investor(&ctx.env); + let project_manager = create_project_manager(&ctx.env); + + let project_id = create_basic_project(&ctx, &investor, &project_manager); + + ctx.env.mock_all_auths(); + + // First deposit + ctx.client + .deposit(&project_id, &investor, &2_000_000_000i128); + + // Second deposit (allowed) + ctx.client + .deposit(&project_id, &investor, &3_000_000_000i128); + + let project = get_project(&ctx, project_id); + let expected_total = 10_000_000_000i128 + 2_000_000_000i128 + 3_000_000_000i128; + assert_eq!(project.total_funding, expected_total); +} + +// ============ NEGATIVE TESTS ============ + +#[test] +#[should_panic(expected = "Invalid project setup parameters")] +fn test_invalid_project_setup() { + let ctx = setup_initialized(); + + let same_address = create_investor(&ctx.env); + + ctx.env.mock_all_auths(); + + // Should panic because investor and project manager are the same + ctx.client.initialize_project( + &same_address, + &same_address, + &String::from_str(&ctx.env, "Invalid Project"), + &String::from_str(&ctx.env, "This should fail"), + &10_000_000_000i128, + &3u32, + &String::from_str(&ctx.env, "solar"), + &10_000_000i128, + ); +} + +#[test] +#[should_panic(expected = "Invalid project setup parameters")] +fn test_invalid_funding_amount() { + let ctx = setup_initialized(); + + let investor = create_investor(&ctx.env); + let project_manager = create_project_manager(&ctx.env); + + ctx.env.mock_all_auths(); + + // Try to create project with zero funding (caught by validation) + ctx.client.initialize_project( + &investor, + &project_manager, + &String::from_str(&ctx.env, "Zero Funding Project"), + &String::from_str(&ctx.env, "Should fail"), + &0i128, + &3u32, + &String::from_str(&ctx.env, "solar"), + &10_000_000i128, + ); +} + +// ============ SCALABILITY TESTS ============ + +#[test] +fn test_high_volume_deposits() { + let ctx = setup_initialized(); + + let investor = create_investor(&ctx.env); + let project_manager = create_project_manager(&ctx.env); + + let project_id = create_basic_project(&ctx, &investor, &project_manager); + + ctx.env.mock_all_auths(); + + let num_deposits = 20; + let deposit_amount = 1_000_000_000i128; + + // Simulate multiple deposits + for _ in 0..num_deposits { + ctx.client.deposit(&project_id, &investor, &deposit_amount); + } + + let project = get_project(&ctx, project_id); + let expected_total = 10_000_000_000i128 + (deposit_amount * num_deposits); + assert_eq!(project.total_funding, expected_total); +} diff --git a/soroban/contracts/energy-project-funding-escrow/src/tests/milestones.rs b/soroban/contracts/energy-project-funding-escrow/src/tests/milestones.rs new file mode 100644 index 0000000..b8957a9 --- /dev/null +++ b/soroban/contracts/energy-project-funding-escrow/src/tests/milestones.rs @@ -0,0 +1,171 @@ +#![cfg(test)] + +use super::utils::*; +use crate::*; +use soroban_sdk::{String, Vec}; + +// ============ MILESTONE CREATION TESTS ============ + +#[test] +fn test_milestone_creation() { + let ctx = setup_initialized(); + + let investor = create_investor(&ctx.env); + let project_manager = create_project_manager(&ctx.env); + + let project_id = create_basic_project(&ctx, &investor, &project_manager); + + let milestone_name = String::from_str(&ctx.env, "Site Preparation"); + let milestone_desc = String::from_str(&ctx.env, "Prepare construction site"); + let funding_percentage = 30u32; + let due_date = ctx.env.ledger().timestamp() + 86400 * 30; // 30 days from now + + let mut required_verifications = Vec::new(&ctx.env); + required_verifications.push_back(String::from_str(&ctx.env, "environmental_impact")); + required_verifications.push_back(String::from_str(&ctx.env, "safety_compliance")); + + ctx.env.mock_all_auths(); + + let milestone_id = ctx.client.create_milestone( + &project_id, + &project_manager, + &milestone_name, + &milestone_desc, + &funding_percentage, + &required_verifications, + &due_date, + &Some(3_000_000i128), + &Some(1_000i128), + ); + + assert_eq!(milestone_id, 1); + + let milestone = get_milestone(&ctx, project_id, milestone_id); + assert_eq!(milestone.funding_percentage, funding_percentage); + assert_eq!(milestone.status, MilestoneStatus::Pending); +} + +// ============ MILESTONE LIFECYCLE TESTS ============ + +#[test] +fn test_milestone_verification_and_completion() { + let ctx = setup_initialized(); + + let investor = create_investor(&ctx.env); + let project_manager = create_project_manager(&ctx.env); + ctx.env.mock_all_auths(); + + let project_id = ctx.client.initialize_project( + &investor, + &project_manager, + &String::from_str(&ctx.env, "Test Project"), + &String::from_str(&ctx.env, "Test Description"), + &10_000_000_000i128, + &2u32, + &String::from_str(&ctx.env, "wind"), + &20_000_000i128, + ); + + let mut required_verifications = Vec::new(&ctx.env); + required_verifications.push_back(String::from_str(&ctx.env, "technical_specification")); + + let milestone_id = ctx.client.create_milestone( + &project_id, + &project_manager, + &String::from_str(&ctx.env, "First Milestone"), + &String::from_str(&ctx.env, "Complete initial phase"), + &50u32, + &required_verifications, + &(ctx.env.ledger().timestamp() + 86400 * 60), + &None, + &None, + ); + + // Start the milestone + ctx.client + .start_milestone(&project_id, &milestone_id, &project_manager); + + let milestone = get_milestone(&ctx, project_id, milestone_id); + assert_eq!(milestone.status, MilestoneStatus::InProgress); + + // Verify the milestone + ctx.client.verify_milestone( + &project_id, + &milestone_id, + &project_manager, + &String::from_str(&ctx.env, "technical_specification"), + &String::from_str(&ctx.env, "Specification approved"), + ); + + let completed_milestone = get_milestone(&ctx, project_id, milestone_id); + assert_eq!(completed_milestone.status, MilestoneStatus::Completed); + + // Check pending release + let pending_release = ctx.client.get_pending_release(&project_id); + assert!(pending_release.is_some()); +} + +// ============ UNAUTHORIZED TESTS ============ + +#[test] +#[should_panic(expected = "Unauthorized")] +fn test_unauthorized_milestone_verification() { + let ctx = setup_initialized(); + + let investor = create_investor(&ctx.env); + let project_manager = create_project_manager(&ctx.env); + let unauthorized_user = create_investor(&ctx.env); // Different user + + let project_id = create_basic_project(&ctx, &investor, &project_manager); + + let mut required_verifications = Vec::new(&ctx.env); + required_verifications.push_back(String::from_str(&ctx.env, "technical_specification")); + + ctx.env.mock_all_auths(); + + let milestone_id = ctx.client.create_milestone( + &project_id, + &project_manager, + &String::from_str(&ctx.env, "Test Milestone"), + &String::from_str(&ctx.env, "Description"), + &50u32, + &required_verifications, + &(ctx.env.ledger().timestamp() + 86400 * 60), + &None, + &None, + ); + + ctx.client + .start_milestone(&project_id, &milestone_id, &project_manager); + + // Unauthorized user tries to verify + ctx.client.verify_milestone( + &project_id, + &milestone_id, + &unauthorized_user, + &String::from_str(&ctx.env, "technical_specification"), + &String::from_str(&ctx.env, "Unauthorized verification"), + ); +} + +#[test] +#[should_panic(expected = "Invalid milestone")] +fn test_verify_invalid_milestone() { + let ctx = setup_initialized(); + + let investor = create_investor(&ctx.env); + let project_manager = create_project_manager(&ctx.env); + + let project_id = create_basic_project(&ctx, &investor, &project_manager); + + ctx.env.mock_all_auths(); + + // Try to verify non-existent milestone + ctx.client.verify_milestone( + &project_id, + &999u32, + &project_manager, + &String::from_str(&ctx.env, "technical_specification"), + &String::from_str(&ctx.env, "Invalid milestone"), + ); +} diff --git a/soroban/contracts/energy-project-funding-escrow/src/tests/mod.rs b/soroban/contracts/energy-project-funding-escrow/src/tests/mod.rs new file mode 100644 index 0000000..7015f06 --- /dev/null +++ b/soroban/contracts/energy-project-funding-escrow/src/tests/mod.rs @@ -0,0 +1,6 @@ +#![cfg(test)] + +pub mod escrow; +pub mod milestones; +pub mod release; +pub mod utils; diff --git a/soroban/contracts/energy-project-funding-escrow/src/tests/release.rs b/soroban/contracts/energy-project-funding-escrow/src/tests/release.rs new file mode 100644 index 0000000..b05a663 --- /dev/null +++ b/soroban/contracts/energy-project-funding-escrow/src/tests/release.rs @@ -0,0 +1,221 @@ +#![cfg(test)] + +use super::utils::*; +use crate::*; +use soroban_sdk::{String, Vec}; + +// ============ FUND RELEASE TESTS ============ + +#[test] +fn test_funds_release() { + let ctx = setup_initialized(); + + let investor = create_investor(&ctx.env); + let project_manager = create_project_manager(&ctx.env); + let total_funding = 20_000_000_000i128; + + ctx.env.mock_all_auths(); + + let project_id = ctx.client.initialize_project( + &investor, + &project_manager, + &String::from_str(&ctx.env, "Funding Test Project"), + &String::from_str(&ctx.env, "Testing fund release"), + &total_funding, + &2u32, + &String::from_str(&ctx.env, "hydro"), + &5_000_000i128, + ); + + let mut required_verifications = Vec::new(&ctx.env); + required_verifications.push_back(String::from_str(&ctx.env, "performance_metrics")); + + let milestone_id = ctx.client.create_milestone( + &project_id, + &project_manager, + &String::from_str(&ctx.env, "Performance Milestone"), + &String::from_str(&ctx.env, "Achieve performance targets"), + &40u32, + &required_verifications, + &(ctx.env.ledger().timestamp() + 86400 * 90), + &Some(2_000_000i128), + &Some(500i128), + ); + + ctx.client + .start_milestone(&project_id, &milestone_id, &project_manager); + + ctx.client.verify_milestone( + &project_id, + &milestone_id, + &project_manager, + &String::from_str(&ctx.env, "performance_metrics"), + &String::from_str(&ctx.env, "Performance targets achieved"), + ); + + let initial_available = ctx.client.get_available_funds(&project_id); + assert_eq!(initial_available, total_funding); + + // Release funds + ctx.client + .release_funds(&project_id, &milestone_id, &investor); + + let project = get_project(&ctx, project_id); + let expected_released = (total_funding * 40) / 100; // 40% of total funding + assert_eq!(project.released_funding, expected_released); + + let remaining_available = ctx.client.get_available_funds(&project_id); + assert_eq!(remaining_available, total_funding - expected_released); +} + +// ============ REFUND TESTS ============ + +#[test] +fn test_refund_request_and_processing() { + let ctx = setup_initialized(); + + let investor = create_investor(&ctx.env); + let project_manager = create_project_manager(&ctx.env); + let total_funding = 15_000_000_000i128; + + ctx.env.mock_all_auths(); + + let project_id = ctx.client.initialize_project( + &investor, + &project_manager, + &String::from_str(&ctx.env, "Refund Test Project"), + &String::from_str(&ctx.env, "Testing refund process"), + &total_funding, + &3u32, + &String::from_str(&ctx.env, "geothermal"), + &8_000_000i128, + ); + + let refund_amount = 5_000_000_000i128; + let reason = String::from_str(&ctx.env, "Project delays due to permit issues"); + + ctx.client + .request_refund(&project_id, &investor, &refund_amount, &reason); + + // Process the refund + ctx.client.process_refund(&project_id, &project_manager); + + let project = get_project(&ctx, project_id); + assert_eq!(project.status, ProjectStatus::Refunded); + assert_eq!(project.escrow_status, EscrowStatus::Refunded); +} + +// ============ EDGE CASE TESTS ============ + +#[test] +#[should_panic(expected = "Milestone not completed")] +fn test_release_without_completed_milestone() { + let ctx = setup_initialized(); + + let investor = create_investor(&ctx.env); + let project_manager = create_project_manager(&ctx.env); + + let project_id = create_basic_project(&ctx, &investor, &project_manager); + + let mut required_verifications = Vec::new(&ctx.env); + required_verifications.push_back(String::from_str(&ctx.env, "technical_specification")); + + ctx.env.mock_all_auths(); + + let milestone_id = ctx.client.create_milestone( + &project_id, + &project_manager, + &String::from_str(&ctx.env, "Incomplete Milestone"), + &String::from_str(&ctx.env, "Not completed"), + &50u32, + &required_verifications, + &(ctx.env.ledger().timestamp() + 86400 * 60), + &None, + &None, + ); + + // Try to release funds without completing milestone + ctx.client + .release_funds(&project_id, &milestone_id, &investor); +} + +#[test] +fn test_refund_workflow() { + let ctx = setup_initialized(); + + let investor = create_investor(&ctx.env); + let project_manager = create_project_manager(&ctx.env); + + let project_id = create_basic_project(&ctx, &investor, &project_manager); + + ctx.env.mock_all_auths(); + + // Create a refund request + ctx.client.request_refund( + &project_id, + &investor, + &5_000_000_000i128, + &String::from_str(&ctx.env, "Project delay"), + ); + + // Process refund + ctx.client.process_refund(&project_id, &project_manager); + + let project = get_project(&ctx, project_id); + assert_eq!(project.status, ProjectStatus::Refunded); +} + +#[test] +fn test_refund_for_failed_milestone() { + let ctx = setup_initialized(); + + let investor = create_investor(&ctx.env); + let project_manager = create_project_manager(&ctx.env); + + let project_id = create_basic_project(&ctx, &investor, &project_manager); + + let mut required_verifications = Vec::new(&ctx.env); + required_verifications.push_back(String::from_str(&ctx.env, "technical_specification")); + + ctx.env.mock_all_auths(); + + let milestone_id = ctx.client.create_milestone( + &project_id, + &project_manager, + &String::from_str(&ctx.env, "Failed Milestone"), + &String::from_str(&ctx.env, "Will fail"), + &30u32, + &required_verifications, + &(ctx.env.ledger().timestamp() + 86400 * 30), + &None, + &None, + ); + + ctx.client + .start_milestone(&project_id, &milestone_id, &project_manager); + + // Mark milestone as failed + ctx.client.fail_milestone( + &project_id, + &milestone_id, + &project_manager, + &String::from_str(&ctx.env, "Technical issues"), + ); + + let milestone = get_milestone(&ctx, project_id, milestone_id); + assert_eq!(milestone.status, MilestoneStatus::Failed); + + // Request refund after milestone failure + ctx.client.request_refund( + &project_id, + &investor, + &3_000_000_000i128, + &String::from_str(&ctx.env, "Milestone failed"), + ); + + // Process refund + ctx.client.process_refund(&project_id, &project_manager); + + let project = get_project(&ctx, project_id); + assert_eq!(project.status, ProjectStatus::Refunded); +} diff --git a/soroban/contracts/energy-project-funding-escrow/src/tests/utils.rs b/soroban/contracts/energy-project-funding-escrow/src/tests/utils.rs new file mode 100644 index 0000000..42d458d --- /dev/null +++ b/soroban/contracts/energy-project-funding-escrow/src/tests/utils.rs @@ -0,0 +1,298 @@ +#![cfg(test)] + +use crate::*; +use soroban_sdk::{testutils::Address as _, Address, Env, String, Vec}; + +// ============ TEST CONTEXT ============ + +pub struct TestContext { + pub env: Env, + pub contract_id: Address, + pub client: EnergyProjectFundingEscrowClient<'static>, +} + +// ============ SETUP FUNCTIONS ============ + +pub fn setup_test() -> TestContext { + let env = Env::default(); + let contract_id = env.register(EnergyProjectFundingEscrow, ()); + let client = EnergyProjectFundingEscrowClient::new(&env, &contract_id); + + TestContext { + env, + contract_id, + client, + } +} + +pub fn setup_initialized() -> TestContext { + let ctx = setup_test(); + ctx.client.initialize(); + ctx +} + +// ============ USER CREATION ============ + +pub fn create_investor(env: &Env) -> Address { + Address::generate(env) +} + +pub fn create_project_manager(env: &Env) -> Address { + Address::generate(env) +} + +// ============ PROJECT HELPERS ============ + +pub fn create_basic_project( + ctx: &TestContext, + investor: &Address, + project_manager: &Address, +) -> u64 { + ctx.env.mock_all_auths(); + + ctx.client.initialize_project( + investor, + project_manager, + &String::from_str(&ctx.env, "Test Project"), + &String::from_str(&ctx.env, "Test Description"), + &10_000_000_000i128, + &3u32, + &String::from_str(&ctx.env, "solar"), + &10_000_000i128, + ) +} + +pub fn create_project_with_params( + ctx: &TestContext, + investor: &Address, + project_manager: &Address, + name: &str, + description: &str, + total_funding: i128, + milestone_count: u32, + energy_type: &str, + expected_capacity: i128, +) -> u64 { + ctx.env.mock_all_auths(); + + ctx.client.initialize_project( + investor, + project_manager, + &String::from_str(&ctx.env, name), + &String::from_str(&ctx.env, description), + &total_funding, + &milestone_count, + &String::from_str(&ctx.env, energy_type), + &expected_capacity, + ) +} + +// ============ MILESTONE HELPERS ============ + +pub fn create_basic_milestone( + ctx: &TestContext, + project_id: u64, + project_manager: &Address, + funding_percentage: u32, +) -> u32 { + ctx.env.mock_all_auths(); + + let mut required_verifications = Vec::new(&ctx.env); + required_verifications.push_back(String::from_str(&ctx.env, "technical_specification")); + + ctx.client.create_milestone( + &project_id, + project_manager, + &String::from_str(&ctx.env, "Test Milestone"), + &String::from_str(&ctx.env, "Test milestone description"), + &funding_percentage, + &required_verifications, + &(ctx.env.ledger().timestamp() + 86400 * 60), + &None, + &None, + ) +} + +pub fn create_milestone_with_verifications( + ctx: &TestContext, + project_id: u64, + project_manager: &Address, + milestone_name: &str, + milestone_desc: &str, + funding_percentage: u32, + verifications: Vec, + due_date: u64, +) -> u32 { + ctx.env.mock_all_auths(); + + ctx.client.create_milestone( + &project_id, + project_manager, + &String::from_str(&ctx.env, milestone_name), + &String::from_str(&ctx.env, milestone_desc), + &funding_percentage, + &verifications, + &due_date, + &None, + &None, + ) +} + +// ============ MILESTONE ACTIONS ============ + +pub fn start_milestone( + ctx: &TestContext, + project_id: u64, + milestone_id: u32, + project_manager: &Address, +) { + ctx.env.mock_all_auths(); + ctx.client + .start_milestone(&project_id, &milestone_id, project_manager); +} + +pub fn verify_milestone( + ctx: &TestContext, + project_id: u64, + milestone_id: u32, + project_manager: &Address, + verification_type: &str, + evidence: &str, +) { + ctx.env.mock_all_auths(); + ctx.client.verify_milestone( + &project_id, + &milestone_id, + project_manager, + &String::from_str(&ctx.env, verification_type), + &String::from_str(&ctx.env, evidence), + ); +} + +// ============ FUND OPERATIONS ============ + +pub fn release_funds(ctx: &TestContext, project_id: u64, milestone_id: u32, investor: &Address) { + ctx.env.mock_all_auths(); + ctx.client + .release_funds(&project_id, &milestone_id, investor); +} + +pub fn request_refund( + ctx: &TestContext, + project_id: u64, + investor: &Address, + amount: i128, + reason: &str, +) { + ctx.env.mock_all_auths(); + ctx.client.request_refund( + &project_id, + investor, + &amount, + &String::from_str(&ctx.env, reason), + ); +} + +pub fn process_refund(ctx: &TestContext, project_id: u64, project_manager: &Address) { + ctx.env.mock_all_auths(); + ctx.client.process_refund(&project_id, project_manager); +} + +// ============ STORAGE ACCESS ============ + +pub fn get_project(ctx: &TestContext, project_id: u64) -> ProjectDetails { + ctx.client.get_project(&project_id) +} + +pub fn get_milestone(ctx: &TestContext, project_id: u64, milestone_id: u32) -> MilestoneDetails { + ctx.client.get_milestone(&project_id, &milestone_id) +} + +pub fn get_next_project_id(ctx: &TestContext) -> u64 { + ctx.env.as_contract(&ctx.contract_id, || { + ctx.env + .storage() + .instance() + .get(&DataKey::NextProjectId) + .unwrap() + }) +} + +// ============ ASSERTIONS ============ + +pub fn assert_project_status(ctx: &TestContext, project_id: u64, expected_status: ProjectStatus) { + let project = get_project(ctx, project_id); + assert_eq!( + project.status, expected_status, + "Project status should match" + ); +} + +pub fn assert_milestone_status( + ctx: &TestContext, + project_id: u64, + milestone_id: u32, + expected_status: MilestoneStatus, +) { + let milestone = get_milestone(ctx, project_id, milestone_id); + assert_eq!( + milestone.status, expected_status, + "Milestone status should match" + ); +} + +pub fn assert_escrow_status(ctx: &TestContext, project_id: u64, expected_status: EscrowStatus) { + let project = get_project(ctx, project_id); + assert_eq!( + project.escrow_status, expected_status, + "Escrow status should match" + ); +} + +// ============ WORKFLOW HELPERS ============ + +/// Complete milestone workflow: create → start → verify +pub fn complete_milestone_workflow( + ctx: &TestContext, + project_id: u64, + project_manager: &Address, + funding_percentage: u32, +) -> u32 { + let milestone_id = create_basic_milestone(ctx, project_id, project_manager, funding_percentage); + start_milestone(ctx, project_id, milestone_id, project_manager); + verify_milestone( + ctx, + project_id, + milestone_id, + project_manager, + "technical_specification", + "Approved", + ); + milestone_id +} + +/// Create project with completed milestone ready for fund release +pub fn setup_project_with_completed_milestone( + ctx: &TestContext, + investor: &Address, + project_manager: &Address, + total_funding: i128, + funding_percentage: u32, +) -> (u64, u32) { + let project_id = create_project_with_params( + ctx, + investor, + project_manager, + "Funded Project", + "Project ready for funding", + total_funding, + 2, + "solar", + 10_000_000, + ); + + let milestone_id = + complete_milestone_workflow(ctx, project_id, project_manager, funding_percentage); + + (project_id, milestone_id) +} diff --git a/soroban/contracts/energy-project-funding-escrow/src/types.rs b/soroban/contracts/energy-project-funding-escrow/src/types.rs index 9d65958..1ebf09b 100644 --- a/soroban/contracts/energy-project-funding-escrow/src/types.rs +++ b/soroban/contracts/energy-project-funding-escrow/src/types.rs @@ -116,4 +116,4 @@ pub struct ProjectMetrics { pub actual_carbon_offset: i128, pub efficiency_rating: u32, pub last_updated: u64, -} \ No newline at end of file +} diff --git a/soroban/contracts/energy-project-funding-escrow/src/utils.rs b/soroban/contracts/energy-project-funding-escrow/src/utils.rs index 8e1bc79..1a80354 100644 --- a/soroban/contracts/energy-project-funding-escrow/src/utils.rs +++ b/soroban/contracts/energy-project-funding-escrow/src/utils.rs @@ -1,5 +1,5 @@ -use soroban_sdk::{Address, Env, String, Vec, vec}; use crate::types::*; +use soroban_sdk::{vec, Address, Env, String, Vec}; const MIN_FUNDING_AMOUNT: i128 = 1_000_000; // 1 XLM (in stroops) const MAX_FUNDING_AMOUNT: i128 = 1_000_000_000_000; // 1M XLM (in stroops) @@ -93,7 +93,11 @@ pub fn calculate_refund_amount( } let completion_percentage = (milestones_completed * 100) / project.milestone_count; - let earned_percentage = if completion_percentage > 100 { 100 } else { completion_percentage }; + let earned_percentage = if completion_percentage > 100 { + 100 + } else { + completion_percentage + }; let earned_amount = (project.total_funding * earned_percentage as i128) / 100; let refundable_amount = project.total_funding - earned_amount; @@ -105,10 +109,7 @@ pub fn calculate_refund_amount( } } -pub fn validate_verification_requirements( - env: &Env, - required_verifications: &Vec, -) -> bool { +pub fn validate_verification_requirements(env: &Env, required_verifications: &Vec) -> bool { if required_verifications.len() == 0 { return false; } @@ -255,7 +256,9 @@ pub fn validate_multisig_setup( pub fn format_project_status_message(status: &ProjectStatus) -> String { match status { ProjectStatus::Active => String::from_str(&Env::default(), "Project is active and funded"), - ProjectStatus::Completed => String::from_str(&Env::default(), "Project successfully completed"), + ProjectStatus::Completed => { + String::from_str(&Env::default(), "Project successfully completed") + } ProjectStatus::Cancelled => String::from_str(&Env::default(), "Project was cancelled"), ProjectStatus::Refunded => String::from_str(&Env::default(), "Project funds were refunded"), } @@ -265,7 +268,11 @@ pub fn format_milestone_status_message(status: &MilestoneStatus) -> String { match status { MilestoneStatus::Pending => String::from_str(&Env::default(), "Milestone awaiting start"), MilestoneStatus::InProgress => String::from_str(&Env::default(), "Milestone in progress"), - MilestoneStatus::Completed => String::from_str(&Env::default(), "Milestone completed successfully"), - MilestoneStatus::Failed => String::from_str(&Env::default(), "Milestone failed to complete"), + MilestoneStatus::Completed => { + String::from_str(&Env::default(), "Milestone completed successfully") + } + MilestoneStatus::Failed => { + String::from_str(&Env::default(), "Milestone failed to complete") + } } -} \ No newline at end of file +}