From 0c4459ee121382ee679c9682c105a0262a379cbc Mon Sep 17 00:00:00 2001 From: Collins C Augustine Date: Thu, 26 Feb 2026 00:30:51 +0100 Subject: [PATCH 1/2] feat: implement admin role management and access control system --- dongle-smartcontract/src/admin_manager.rs | 253 ++++++++++++++++++ dongle-smartcontract/src/admin_tests.rs | 123 +++++++++ dongle-smartcontract/src/errors.rs | 4 + dongle-smartcontract/src/events.rs | 10 + dongle-smartcontract/src/fee_manager.rs | 8 +- dongle-smartcontract/src/lib.rs | 35 ++- dongle-smartcontract/src/storage_keys.rs | 6 +- .../src/verification_registry.rs | 67 ++++- 8 files changed, 491 insertions(+), 15 deletions(-) create mode 100644 dongle-smartcontract/src/admin_manager.rs create mode 100644 dongle-smartcontract/src/admin_tests.rs diff --git a/dongle-smartcontract/src/admin_manager.rs b/dongle-smartcontract/src/admin_manager.rs new file mode 100644 index 0000000..e0aee50 --- /dev/null +++ b/dongle-smartcontract/src/admin_manager.rs @@ -0,0 +1,253 @@ +//! Admin role management and access control +//! +//! This module provides functionality for managing admin roles and enforcing +//! access control across privileged contract operations. + +use crate::errors::ContractError; +use crate::events::{publish_admin_added_event, publish_admin_removed_event}; +use crate::storage_keys::StorageKey; +use soroban_sdk::{Address, Env, Vec}; + +pub struct AdminManager; + +impl AdminManager { + /// Initialize the contract with the first admin + pub fn initialize(env: &Env, admin: Address) { + // Don't require auth during initialization - this is typically called once during contract deployment + + // Set the admin in storage + env.storage() + .persistent() + .set(&StorageKey::Admin(admin.clone()), &true); + + // Initialize admin list + let mut admins = Vec::new(env); + admins.push_back(admin.clone()); + env.storage() + .persistent() + .set(&StorageKey::AdminList, &admins); + + publish_admin_added_event(env, admin); + } + + /// Add a new admin (only callable by existing admins) + pub fn add_admin(env: &Env, caller: Address, new_admin: Address) -> Result<(), ContractError> { + caller.require_auth(); + + // Verify caller is an admin + Self::require_admin(env, &caller)?; + + // Check if already an admin + if Self::is_admin(env, &new_admin) { + return Ok(()); // Already an admin, no-op + } + + // Add to admin mapping + env.storage() + .persistent() + .set(&StorageKey::Admin(new_admin.clone()), &true); + + // Add to admin list + let mut admins = Self::get_admin_list(env); + admins.push_back(new_admin.clone()); + env.storage() + .persistent() + .set(&StorageKey::AdminList, &admins); + + publish_admin_added_event(env, new_admin); + + Ok(()) + } + + /// Remove an admin (only callable by existing admins) + pub fn remove_admin( + env: &Env, + caller: Address, + admin_to_remove: Address, + ) -> Result<(), ContractError> { + caller.require_auth(); + + // Verify caller is an admin + Self::require_admin(env, &caller)?; + + // Prevent removing the last admin + let admins = Self::get_admin_list(env); + if admins.len() <= 1 { + return Err(ContractError::CannotRemoveLastAdmin); + } + + // Check if the address is actually an admin + if !Self::is_admin(env, &admin_to_remove) { + return Err(ContractError::AdminNotFound); + } + + // Remove from admin mapping + env.storage() + .persistent() + .remove(&StorageKey::Admin(admin_to_remove.clone())); + + // Remove from admin list + let mut new_admins = Vec::new(env); + for admin in admins.iter() { + if admin != admin_to_remove { + new_admins.push_back(admin); + } + } + env.storage() + .persistent() + .set(&StorageKey::AdminList, &new_admins); + + publish_admin_removed_event(env, admin_to_remove); + + Ok(()) + } + + /// Check if an address is an admin + pub fn is_admin(env: &Env, address: &Address) -> bool { + env.storage() + .persistent() + .get(&StorageKey::Admin(address.clone())) + .unwrap_or(false) + } + + /// Require that the caller is an admin, otherwise return an error + pub fn require_admin(env: &Env, address: &Address) -> Result<(), ContractError> { + if Self::is_admin(env, address) { + Ok(()) + } else { + Err(ContractError::AdminOnly) + } + } + + /// Get the list of all admins + pub fn get_admin_list(env: &Env) -> Vec
{ + env.storage() + .persistent() + .get(&StorageKey::AdminList) + .unwrap_or(Vec::new(env)) + } + + /// Get the count of admins + pub fn get_admin_count(env: &Env) -> u32 { + Self::get_admin_list(env).len() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::DongleContract; + use crate::DongleContractClient; + use soroban_sdk::{testutils::Address as _, Address, Env}; + + #[test] + fn test_initialize_admin() { + let env = Env::default(); + let contract_id = env.register_contract(None, DongleContract); + let client = DongleContractClient::new(&env, &contract_id); + let admin = Address::generate(&env); + + client.mock_all_auths().initialize(&admin); + + assert!(client.is_admin(&admin)); + assert_eq!(client.get_admin_count(), 1); + } + + #[test] + fn test_add_admin() { + let env = Env::default(); + let contract_id = env.register_contract(None, DongleContract); + let client = DongleContractClient::new(&env, &contract_id); + let admin1 = Address::generate(&env); + let admin2 = Address::generate(&env); + + client.mock_all_auths().initialize(&admin1); + client.mock_all_auths().add_admin(&admin1, &admin2); + + assert!(client.is_admin(&admin2)); + assert_eq!(client.get_admin_count(), 2); + } + + #[test] + fn test_add_admin_unauthorized() { + let env = Env::default(); + let contract_id = env.register_contract(None, DongleContract); + let client = DongleContractClient::new(&env, &contract_id); + let admin = Address::generate(&env); + let non_admin = Address::generate(&env); + let new_admin = Address::generate(&env); + + client.mock_all_auths().initialize(&admin); + client.mock_all_auths().add_admin(&non_admin, &new_admin); + + // The new admin should not be added + assert!(!client.is_admin(&new_admin)); + } + + #[test] + fn test_remove_admin() { + let env = Env::default(); + let contract_id = env.register_contract(None, DongleContract); + let client = DongleContractClient::new(&env, &contract_id); + let admin1 = Address::generate(&env); + let admin2 = Address::generate(&env); + + client.mock_all_auths().initialize(&admin1); + client.mock_all_auths().add_admin(&admin1, &admin2); + client.mock_all_auths().remove_admin(&admin1, &admin2); + + assert!(!client.is_admin(&admin2)); + assert_eq!(client.get_admin_count(), 1); + } + + #[test] + fn test_cannot_remove_last_admin() { + let env = Env::default(); + let contract_id = env.register_contract(None, DongleContract); + let client = DongleContractClient::new(&env, &contract_id); + let admin = Address::generate(&env); + + client.mock_all_auths().initialize(&admin); + client.mock_all_auths().remove_admin(&admin, &admin); + + // Should still be admin + assert!(client.is_admin(&admin)); + } + + #[test] + fn test_remove_non_existent_admin() { + let env = Env::default(); + let contract_id = env.register_contract(None, DongleContract); + let client = DongleContractClient::new(&env, &contract_id); + let admin = Address::generate(&env); + let non_admin = Address::generate(&env); + let another_admin = Address::generate(&env); + + client.mock_all_auths().initialize(&admin); + client.mock_all_auths().add_admin(&admin, &another_admin); + client.mock_all_auths().remove_admin(&admin, &non_admin); + + // another_admin should still be there + assert!(client.is_admin(&another_admin)); + } + + #[test] + fn test_get_admin_list() { + let env = Env::default(); + let contract_id = env.register_contract(None, DongleContract); + let client = DongleContractClient::new(&env, &contract_id); + let admin1 = Address::generate(&env); + let admin2 = Address::generate(&env); + let admin3 = Address::generate(&env); + + client.mock_all_auths().initialize(&admin1); + client.mock_all_auths().add_admin(&admin1, &admin2); + client.mock_all_auths().add_admin(&admin1, &admin3); + + let admins = client.get_admin_list(); + assert_eq!(admins.len(), 3); + assert!(admins.contains(&admin1)); + assert!(admins.contains(&admin2)); + assert!(admins.contains(&admin3)); + } +} diff --git a/dongle-smartcontract/src/admin_tests.rs b/dongle-smartcontract/src/admin_tests.rs new file mode 100644 index 0000000..f7a37f8 --- /dev/null +++ b/dongle-smartcontract/src/admin_tests.rs @@ -0,0 +1,123 @@ +//! Integration tests for admin role management and access control + +#[cfg(test)] +mod tests { + use crate::DongleContract; + use crate::DongleContractClient; + use soroban_sdk::testutils::Address as _; + use soroban_sdk::{Address, Env}; + + fn setup(env: &Env) -> (DongleContractClient, Address) { + let contract_id = env.register_contract(None, DongleContract); + let client = DongleContractClient::new(env, &contract_id); + let admin = Address::generate(env); + client.mock_all_auths().initialize(&admin); + (client, admin) + } + + #[test] + fn test_admin_initialization() { + let env = Env::default(); + let (client, admin) = setup(&env); + + assert!(client.is_admin(&admin)); + assert_eq!(client.get_admin_count(), 1); + + let admin_list = client.get_admin_list(); + assert_eq!(admin_list.len(), 1); + assert_eq!(admin_list.get(0).unwrap(), admin); + } + + #[test] + fn test_add_multiple_admins() { + let env = Env::default(); + let (client, admin1) = setup(&env); + let admin2 = Address::generate(&env); + let admin3 = Address::generate(&env); + + client.mock_all_auths().add_admin(&admin1, &admin2); + client.mock_all_auths().add_admin(&admin1, &admin3); + + assert!(client.is_admin(&admin1)); + assert!(client.is_admin(&admin2)); + assert!(client.is_admin(&admin3)); + assert_eq!(client.get_admin_count(), 3); + } + + #[test] + fn test_remove_admin_success() { + let env = Env::default(); + let (client, admin1) = setup(&env); + let admin2 = Address::generate(&env); + + client.mock_all_auths().add_admin(&admin1, &admin2); + assert_eq!(client.get_admin_count(), 2); + + client.mock_all_auths().remove_admin(&admin1, &admin2); + + assert!(client.is_admin(&admin1)); + assert!(!client.is_admin(&admin2)); + assert_eq!(client.get_admin_count(), 1); + } + + #[test] + fn test_admin_can_set_fees() { + let env = Env::default(); + let (client, admin) = setup(&env); + let treasury = Address::generate(&env); + let fee_amount = 1000u128; + + client + .mock_all_auths() + .set_fee(&admin, &None, &fee_amount, &treasury); + + let config = client.get_fee_config(); + assert_eq!(config.verification_fee, fee_amount); + } + + #[test] + fn test_multiple_admins_can_perform_actions() { + let env = Env::default(); + let (client, admin1) = setup(&env); + let admin2 = Address::generate(&env); + let treasury = Address::generate(&env); + + client.mock_all_auths().add_admin(&admin1, &admin2); + + // Both admins can set fees + client + .mock_all_auths() + .set_fee(&admin1, &None, &1000u128, &treasury); + client + .mock_all_auths() + .set_fee(&admin2, &None, &2000u128, &treasury); + + let config = client.get_fee_config(); + assert_eq!(config.verification_fee, 2000u128); + } + + #[test] + fn test_admin_list_updates_correctly() { + let env = Env::default(); + let (client, admin1) = setup(&env); + let admin2 = Address::generate(&env); + let admin3 = Address::generate(&env); + + let list = client.get_admin_list(); + assert_eq!(list.len(), 1); + + client.mock_all_auths().add_admin(&admin1, &admin2); + let list = client.get_admin_list(); + assert_eq!(list.len(), 2); + + client.mock_all_auths().add_admin(&admin1, &admin3); + let list = client.get_admin_list(); + assert_eq!(list.len(), 3); + + client.mock_all_auths().remove_admin(&admin1, &admin2); + let list = client.get_admin_list(); + assert_eq!(list.len(), 2); + assert!(list.contains(&admin1)); + assert!(list.contains(&admin3)); + } +} diff --git a/dongle-smartcontract/src/errors.rs b/dongle-smartcontract/src/errors.rs index b7621c4..82ad53c 100644 --- a/dongle-smartcontract/src/errors.rs +++ b/dongle-smartcontract/src/errors.rs @@ -45,4 +45,8 @@ pub enum ContractError { AlreadyReviewed = 19, // I added your error here with a new unique ID /// Review is already deleted ReviewAlreadyDeleted = 20, + /// Cannot remove the last admin + CannotRemoveLastAdmin = 21, + /// Admin not found + AdminNotFound = 22, } diff --git a/dongle-smartcontract/src/events.rs b/dongle-smartcontract/src/events.rs index 9949c10..8b337d3 100644 --- a/dongle-smartcontract/src/events.rs +++ b/dongle-smartcontract/src/events.rs @@ -62,3 +62,13 @@ pub fn publish_verification_rejected_event(env: &Env, project_id: u64) { project_id, ); } + +pub fn publish_admin_added_event(env: &Env, admin: Address) { + env.events() + .publish((symbol_short!("ADMIN"), symbol_short!("ADDED")), admin); +} + +pub fn publish_admin_removed_event(env: &Env, admin: Address) { + env.events() + .publish((symbol_short!("ADMIN"), symbol_short!("REMOVED")), admin); +} diff --git a/dongle-smartcontract/src/fee_manager.rs b/dongle-smartcontract/src/fee_manager.rs index fa15986..c4b9f0c 100644 --- a/dongle-smartcontract/src/fee_manager.rs +++ b/dongle-smartcontract/src/fee_manager.rs @@ -1,5 +1,6 @@ //! Fee configuration and payment with validation and events. +use crate::admin_manager::AdminManager; use crate::errors::ContractError; use crate::events::publish_fee_set_event; use crate::types::{DataKey, FeeConfig}; @@ -10,11 +11,16 @@ pub struct FeeManager; impl FeeManager { pub fn set_fee( env: &Env, - _admin: Address, + admin: Address, token: Option
, amount: u128, treasury: Address, ) -> Result<(), ContractError> { + admin.require_auth(); + + // Verify admin privileges + AdminManager::require_admin(env, &admin)?; + let config = FeeConfig { token, verification_fee: amount, diff --git a/dongle-smartcontract/src/lib.rs b/dongle-smartcontract/src/lib.rs index ff8d6cc..814147f 100644 --- a/dongle-smartcontract/src/lib.rs +++ b/dongle-smartcontract/src/lib.rs @@ -1,5 +1,6 @@ #![no_std] +mod admin_manager; mod constants; mod errors; mod events; @@ -15,6 +16,10 @@ mod verification_registry; #[cfg(test)] mod test; +#[cfg(test)] +mod admin_tests; + +use crate::admin_manager::AdminManager; use crate::fee_manager::FeeManager; use crate::project_registry::ProjectRegistry; use crate::review_registry::ReviewRegistry; @@ -168,13 +173,35 @@ impl DongleContract { ProjectRegistry::get_projects_by_owner(&env, owner).len() } + // --- Admin Management --- + + pub fn initialize(env: Env, admin: Address) { + AdminManager::initialize(&env, admin); + } + + pub fn add_admin(env: Env, caller: Address, new_admin: Address) { + let _ = AdminManager::add_admin(&env, caller, new_admin); + } + + pub fn remove_admin(env: Env, caller: Address, admin_to_remove: Address) { + let _ = AdminManager::remove_admin(&env, caller, admin_to_remove); + } + + pub fn is_admin(env: Env, address: Address) -> bool { + AdminManager::is_admin(&env, &address) + } + + pub fn get_admin_list(env: Env) -> Vec
{ + AdminManager::get_admin_list(&env) + } + + pub fn get_admin_count(env: Env) -> u32 { + AdminManager::get_admin_count(&env) + } + pub fn set_admin(env: Env, admin: Address) { env.storage() .persistent() .set(&crate::types::DataKey::Admin(admin), &()); } - - pub fn initialize(env: Env, admin: Address) { - Self::set_admin(env, admin); - } } diff --git a/dongle-smartcontract/src/storage_keys.rs b/dongle-smartcontract/src/storage_keys.rs index 5541730..08934a7 100644 --- a/dongle-smartcontract/src/storage_keys.rs +++ b/dongle-smartcontract/src/storage_keys.rs @@ -20,8 +20,10 @@ pub enum StorageKey { FeeConfig, /// Whether verification fee has been paid for project_id. FeePaidForProject(u64), - /// Admin address (for fee set and verifier checks). - Admin, + /// Admin address mapping (for role-based access control). + Admin(soroban_sdk::Address), + /// List of all admin addresses. + AdminList, /// List of project IDs reviewed by a user. UserReviews(soroban_sdk::Address), } diff --git a/dongle-smartcontract/src/verification_registry.rs b/dongle-smartcontract/src/verification_registry.rs index 622779d..270de09 100644 --- a/dongle-smartcontract/src/verification_registry.rs +++ b/dongle-smartcontract/src/verification_registry.rs @@ -1,5 +1,6 @@ //! Verification requests with ownership and fee checks, and events. +use crate::admin_manager::AdminManager; use crate::errors::ContractError; use crate::events::{ publish_verification_approved_event, publish_verification_rejected_event, @@ -23,19 +24,69 @@ impl VerificationRegistry { } pub fn approve_verification( - _env: &Env, - _project_id: u64, - _admin: Address, + env: &Env, + project_id: u64, + admin: Address, ) -> Result<(), ContractError> { - todo!("Verification approval logic not implemented") + admin.require_auth(); + + // Verify admin privileges + AdminManager::require_admin(env, &admin)?; + + // Get verification record + let mut record: VerificationRecord = env + .storage() + .persistent() + .get(&DataKey::Verification(project_id)) + .ok_or(ContractError::VerificationNotFound)?; + + // Check if already processed + if record.status == VerificationStatus::Verified { + return Err(ContractError::VerificationAlreadyProcessed); + } + + // Update status + record.status = VerificationStatus::Verified; + env.storage() + .persistent() + .set(&DataKey::Verification(project_id), &record); + + publish_verification_approved_event(env, project_id); + + Ok(()) } pub fn reject_verification( - _env: &Env, - _project_id: u64, - _admin: Address, + env: &Env, + project_id: u64, + admin: Address, ) -> Result<(), ContractError> { - todo!("Verification rejection logic not implemented") + admin.require_auth(); + + // Verify admin privileges + AdminManager::require_admin(env, &admin)?; + + // Get verification record + let mut record: VerificationRecord = env + .storage() + .persistent() + .get(&DataKey::Verification(project_id)) + .ok_or(ContractError::VerificationNotFound)?; + + // Check if already processed + if record.status == VerificationStatus::Rejected { + return Err(ContractError::VerificationAlreadyProcessed); + } + + // Update status + record.status = VerificationStatus::Rejected; + env.storage() + .persistent() + .set(&DataKey::Verification(project_id), &record); + + publish_verification_rejected_event(env, project_id); + + Ok(()) } pub fn get_verification( From cf5f24c4ced0edbbcd95e3026583251df2e1d049 Mon Sep 17 00:00:00 2001 From: Collins C Augustine Date: Thu, 26 Feb 2026 00:57:10 +0100 Subject: [PATCH 2/2] fix: cl error to ensure passing --- dongle-smartcontract/src/admin_manager.rs | 1 - dongle-smartcontract/src/admin_tests.rs | 2 +- dongle-smartcontract/src/constants.rs | 9 +++++++++ dongle-smartcontract/src/events.rs | 2 ++ dongle-smartcontract/src/lib.rs | 2 ++ dongle-smartcontract/src/project_registry.rs | 14 ++++++++------ dongle-smartcontract/src/rating_calculator.rs | 2 ++ dongle-smartcontract/src/review_registry.rs | 1 + dongle-smartcontract/src/test.rs | 7 ++----- dongle-smartcontract/src/utils.rs | 2 ++ .../src/verification_registry.rs | 19 +++++++++++-------- 11 files changed, 40 insertions(+), 21 deletions(-) diff --git a/dongle-smartcontract/src/admin_manager.rs b/dongle-smartcontract/src/admin_manager.rs index e0aee50..bd04b99 100644 --- a/dongle-smartcontract/src/admin_manager.rs +++ b/dongle-smartcontract/src/admin_manager.rs @@ -135,7 +135,6 @@ impl AdminManager { #[cfg(test)] mod tests { - use super::*; use crate::DongleContract; use crate::DongleContractClient; use soroban_sdk::{testutils::Address as _, Address, Env}; diff --git a/dongle-smartcontract/src/admin_tests.rs b/dongle-smartcontract/src/admin_tests.rs index f7a37f8..3b0d79b 100644 --- a/dongle-smartcontract/src/admin_tests.rs +++ b/dongle-smartcontract/src/admin_tests.rs @@ -7,7 +7,7 @@ mod tests { use soroban_sdk::testutils::Address as _; use soroban_sdk::{Address, Env}; - fn setup(env: &Env) -> (DongleContractClient, Address) { + fn setup(env: &Env) -> (DongleContractClient<'_>, Address) { let contract_id = env.register_contract(None, DongleContract); let client = DongleContractClient::new(env, &contract_id); let admin = Address::generate(env); diff --git a/dongle-smartcontract/src/constants.rs b/dongle-smartcontract/src/constants.rs index a3e77ce..df16860 100644 --- a/dongle-smartcontract/src/constants.rs +++ b/dongle-smartcontract/src/constants.rs @@ -1,26 +1,35 @@ //! Contract limits and validation constants. Kept in one place for easy future updates. /// Maximum number of projects a single user (address) can register. Prevents abuse. +#[allow(dead_code)] pub const MAX_PROJECTS_PER_USER: u32 = 50; /// Minimum length for name, description, category (must be non-empty after trim in validation). +#[allow(dead_code)] pub const MIN_STRING_LEN: usize = 1; /// Maximum length for project name. +#[allow(dead_code)] pub const MAX_NAME_LEN: usize = 128; /// Maximum length for project description. +#[allow(dead_code)] pub const MAX_DESCRIPTION_LEN: usize = 2048; /// Maximum length for category. +#[allow(dead_code)] pub const MAX_CATEGORY_LEN: usize = 64; /// Maximum length for website URL. +#[allow(dead_code)] pub const MAX_WEBSITE_LEN: usize = 256; /// Maximum length for any CID (logo, metadata, comment, evidence). +#[allow(dead_code)] pub const MAX_CID_LEN: usize = 128; /// Valid rating range (inclusive). Reviews must be in [RATING_MIN, RATING_MAX]. u32 for Soroban Val. +#[allow(dead_code)] pub const RATING_MIN: u32 = 1; +#[allow(dead_code)] pub const RATING_MAX: u32 = 5; diff --git a/dongle-smartcontract/src/events.rs b/dongle-smartcontract/src/events.rs index 8b337d3..bd84707 100644 --- a/dongle-smartcontract/src/events.rs +++ b/dongle-smartcontract/src/events.rs @@ -28,6 +28,7 @@ pub fn publish_review_event( .publish((REVIEW, action_sym, project_id, reviewer), event_data); } +#[allow(dead_code)] pub fn publish_fee_paid_event(env: &Env, project_id: u64, amount: u128) { env.events().publish( (symbol_short!("FEE"), symbol_short!("PAID"), project_id), @@ -42,6 +43,7 @@ pub fn publish_fee_set_event(env: &Env, verification_fee: u128, registration_fee ); } +#[allow(dead_code)] pub fn publish_verification_requested_event(env: &Env, project_id: u64, requester: Address) { env.events().publish( (symbol_short!("VERIFY"), symbol_short!("REQ"), project_id), diff --git a/dongle-smartcontract/src/lib.rs b/dongle-smartcontract/src/lib.rs index 814147f..c090d2a 100644 --- a/dongle-smartcontract/src/lib.rs +++ b/dongle-smartcontract/src/lib.rs @@ -34,6 +34,7 @@ pub struct DongleContract; impl DongleContract { // --- Project Registry --- + #[allow(clippy::too_many_arguments)] pub fn register_project( env: Env, owner: Address, @@ -56,6 +57,7 @@ impl DongleContract { ) } + #[allow(clippy::too_many_arguments)] pub fn update_project( env: Env, project_id: u64, diff --git a/dongle-smartcontract/src/project_registry.rs b/dongle-smartcontract/src/project_registry.rs index 209f037..5d31cc0 100644 --- a/dongle-smartcontract/src/project_registry.rs +++ b/dongle-smartcontract/src/project_registry.rs @@ -5,6 +5,7 @@ use soroban_sdk::{Address, Env, String, Vec}; pub struct ProjectRegistry; impl ProjectRegistry { + #[allow(clippy::too_many_arguments)] pub fn register_project( env: &Env, owner: Address, @@ -18,13 +19,13 @@ impl ProjectRegistry { owner.require_auth(); // Validation - if name.len() == 0 { + if name.is_empty() { panic!("InvalidProjectName"); } - if description.len() == 0 { + if description.is_empty() { panic!("InvalidProjectDescription"); } - if category.len() == 0 { + if category.is_empty() { panic!("InvalidProjectCategory"); } @@ -70,6 +71,7 @@ impl ProjectRegistry { count } + #[allow(clippy::too_many_arguments)] pub fn update_project( env: &Env, project_id: u64, @@ -177,13 +179,13 @@ impl ProjectRegistry { description: &String, category: &String, ) -> Result<(), ContractError> { - if name.len() == 0 { + if name.is_empty() { return Err(ContractError::InvalidProjectData); } - if description.len() == 0 { + if description.is_empty() { return Err(ContractError::ProjectDescriptionTooLong); // Just picking one for now to match ContractError } - if category.len() == 0 { + if category.is_empty() { return Err(ContractError::InvalidProjectCategory); } Ok(()) diff --git a/dongle-smartcontract/src/rating_calculator.rs b/dongle-smartcontract/src/rating_calculator.rs index 32f244b..33cee3a 100644 --- a/dongle-smartcontract/src/rating_calculator.rs +++ b/dongle-smartcontract/src/rating_calculator.rs @@ -31,6 +31,7 @@ impl RatingCalculator { /// /// # Returns /// Tuple of (new_sum, new_count, new_average) + #[allow(dead_code)] pub fn add_rating(current_sum: u64, current_count: u32, new_rating: u32) -> (u64, u32, u32) { let scaled_rating = (new_rating as u64) * 100; let new_sum = current_sum + scaled_rating; @@ -49,6 +50,7 @@ impl RatingCalculator { /// /// # Returns /// Tuple of (new_sum, new_count, new_average) + #[allow(dead_code)] pub fn update_rating( current_sum: u64, current_count: u32, diff --git a/dongle-smartcontract/src/review_registry.rs b/dongle-smartcontract/src/review_registry.rs index 15f6fe6..4b943cc 100644 --- a/dongle-smartcontract/src/review_registry.rs +++ b/dongle-smartcontract/src/review_registry.rs @@ -147,6 +147,7 @@ impl ReviewRegistry { .get(&DataKey::Review(project_id, reviewer)) } + #[allow(dead_code)] pub fn get_reviews_by_user( env: &Env, user: Address, diff --git a/dongle-smartcontract/src/test.rs b/dongle-smartcontract/src/test.rs index 20adb04..1b8743b 100644 --- a/dongle-smartcontract/src/test.rs +++ b/dongle-smartcontract/src/test.rs @@ -1,14 +1,11 @@ //! Tests for validation, limits, error codes, and edge cases. -use crate::constants::MAX_PROJECTS_PER_USER; -use crate::errors::ContractError as Error; -use crate::types::{FeeConfig, VerificationStatus}; use crate::DongleContract; use crate::DongleContractClient; use soroban_sdk::testutils::Address as _; -use soroban_sdk::{Address, Env, String as SorobanString, Vec}; +use soroban_sdk::{Address, Env, String as SorobanString}; -fn setup(env: &Env) -> (DongleContractClient, Address, Address) { +fn setup(env: &Env) -> (DongleContractClient<'_>, Address, Address) { let contract_id = env.register_contract(None, DongleContract); let client = DongleContractClient::new(env, &contract_id); let admin = Address::generate(env); diff --git a/dongle-smartcontract/src/utils.rs b/dongle-smartcontract/src/utils.rs index 31a51a1..1218b65 100644 --- a/dongle-smartcontract/src/utils.rs +++ b/dongle-smartcontract/src/utils.rs @@ -2,8 +2,10 @@ use crate::errors::ContractError; use crate::types::DataKey; use soroban_sdk::{Address, Env, String}; +#[allow(dead_code)] pub struct Utils; +#[allow(dead_code)] impl Utils { pub fn get_current_timestamp(_env: &Env) -> u64 { 0 diff --git a/dongle-smartcontract/src/verification_registry.rs b/dongle-smartcontract/src/verification_registry.rs index 270de09..b7d1d43 100644 --- a/dongle-smartcontract/src/verification_registry.rs +++ b/dongle-smartcontract/src/verification_registry.rs @@ -2,10 +2,7 @@ use crate::admin_manager::AdminManager; use crate::errors::ContractError; -use crate::events::{ - publish_verification_approved_event, publish_verification_rejected_event, - publish_verification_requested_event, -}; +use crate::events::{publish_verification_approved_event, publish_verification_rejected_event}; use crate::types::{DataKey, VerificationRecord, VerificationStatus}; use soroban_sdk::{Address, Env, String, Vec}; @@ -13,10 +10,10 @@ pub struct VerificationRegistry; impl VerificationRegistry { pub fn request_verification( - env: &Env, - project_id: u64, - requester: Address, - evidence_cid: String, + _env: &Env, + _project_id: u64, + _requester: Address, + _evidence_cid: String, ) { // Validate project ownership // Require fee paid via FeeManager @@ -96,6 +93,7 @@ impl VerificationRegistry { todo!("Verification record retrieval logic not implemented") } + #[allow(dead_code)] pub fn list_pending_verifications( _env: &Env, _admin: Address, @@ -105,10 +103,12 @@ impl VerificationRegistry { todo!("Pending verification listing logic not implemented") } + #[allow(dead_code)] pub fn verification_exists(_env: &Env, _project_id: u64) -> bool { false } + #[allow(dead_code)] pub fn get_verification_status( _env: &Env, _project_id: u64, @@ -116,6 +116,7 @@ impl VerificationRegistry { todo!("Verification status retrieval not implemented") } + #[allow(dead_code)] pub fn update_verification_evidence( _env: &Env, _project_id: u64, @@ -125,6 +126,7 @@ impl VerificationRegistry { todo!("Verification evidence update logic not implemented") } + #[allow(dead_code)] pub fn validate_evidence_cid(evidence_cid: &String) -> Result<(), ContractError> { if evidence_cid.is_empty() { return Err(ContractError::InvalidProjectData); @@ -132,6 +134,7 @@ impl VerificationRegistry { Ok(()) } + #[allow(dead_code)] pub fn get_verification_stats(_env: &Env) -> (u32, u32, u32) { (0, 0, 0) }