From cc4481ec3f917f45635345c414b55bf8c4aaebc2 Mon Sep 17 00:00:00 2001 From: Okoli Johnson <41733104+brisstone@users.noreply.github.com> Date: Tue, 7 Oct 2025 00:18:18 +0100 Subject: [PATCH 1/4] chore: add relevant error/event types --- contracts/boundless/src/datatypes.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/contracts/boundless/src/datatypes.rs b/contracts/boundless/src/datatypes.rs index 43a0593..942c16d 100644 --- a/contracts/boundless/src/datatypes.rs +++ b/contracts/boundless/src/datatypes.rs @@ -181,6 +181,10 @@ pub enum BoundlessError { EscrowAlreadyLinked = 14, /// Escrow contract not found EscrowNotFound = 15, + /// Campaign not found + CampaignAlreadyCompleted = 16, + /// Operation not allowed in current campaign state + InvalidCampaignState = 17, } // Events #[contractevent] @@ -202,3 +206,10 @@ pub struct CampaignStatusUpdated { pub status: Status, pub admin: Address, } + +#[contractevent] +pub struct CampaignCompleted { + pub campaign_id: u64, + pub completed_by: Address, + pub completed_at: u64, +} From 8c4561ef29ac4190523cc37473b74b3b1558f275 Mon Sep 17 00:00:00 2001 From: Okoli Johnson <41733104+brisstone@users.noreply.github.com> Date: Tue, 7 Oct 2025 00:19:44 +0100 Subject: [PATCH 2/4] feat: add helper functions --- contracts/boundless/src/helper.rs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 contracts/boundless/src/helper.rs diff --git a/contracts/boundless/src/helper.rs b/contracts/boundless/src/helper.rs new file mode 100644 index 0000000..68656ce --- /dev/null +++ b/contracts/boundless/src/helper.rs @@ -0,0 +1,24 @@ +use crate::datatypes::{BoundlessError, CampaignCompleted, DataKey}; +use soroban_sdk::{Env, Address}; + +// Helper function to check platform admin +pub fn is_platform_admin(env: &Env, address: &Address) -> Result { + let admin: Address = env + .storage() + .persistent() + .get(&DataKey::Admin) + .ok_or(BoundlessError::Unauthorized)?; + + Ok(&admin == address) +} + +// Helper function to emit event +pub fn emit_campaign_completed_event(env: &Env, campaign_id: u64, completed_by: Address) { + let event_data = CampaignCompleted { + campaign_id, + completed_by, + completed_at: env.ledger().timestamp(), + }; + + event_data.publish(env); +} From 81f246bb2564fd2822ac0b13e7184ea310710d62 Mon Sep 17 00:00:00 2001 From: Okoli Johnson <41733104+brisstone@users.noreply.github.com> Date: Tue, 7 Oct 2025 00:21:28 +0100 Subject: [PATCH 3/4] feat: implement complete_campaign --- contracts/boundless/src/logic/campaign.rs | 44 +++++++++++++++++++---- 1 file changed, 37 insertions(+), 7 deletions(-) diff --git a/contracts/boundless/src/logic/campaign.rs b/contracts/boundless/src/logic/campaign.rs index 67b52c6..de8d6a2 100644 --- a/contracts/boundless/src/logic/campaign.rs +++ b/contracts/boundless/src/logic/campaign.rs @@ -1,7 +1,8 @@ use crate::datatypes::{ Backer, BoundlessError, Campaign, CampaignCancelled, CampaignFunded, CampaignStatusUpdated, - Milestone, Status, + Milestone, Status, DataKey }; +use crate::helper::{emit_campaign_completed_event, is_platform_admin}; use crate::interface::{CampaignManagement, ContractManagement}; use crate::{BoundlessContract, BoundlessContractArgs, BoundlessContractClient}; use soroban_sdk::{contractimpl, Address, Env, Symbol, Vec}; @@ -80,12 +81,41 @@ impl CampaignManagement for BoundlessContract { } fn complete_campaign(env: Env, campaign_id: u64, admin: Address) -> Result<(), BoundlessError> { - // TODO: complete campaign logic - // - Verify admin authorization - // - Get campaign from storage - // - Update status to Completed - // - Store updated campaign - // - Emit completion event + // Verify admin authorization + admin.require_auth(); + + // Get campaign from storage + let mut campaign: Campaign = env + .storage() + .persistent() + .get(&DataKey::Campaign(campaign_id)) + .ok_or(BoundlessError::CampaignNotFound)?; + + // Check campaign status + match campaign.status { + Status::Completed => return Err(BoundlessError::CampaignAlreadyCompleted), + Status::Pending | Status::Active => { + // Proceed for active campaigns + }, + _ => return Err(BoundlessError::InvalidCampaignState), + } + + // Verify admin has permission + if campaign.owner != admin && !is_platform_admin(&env, &admin)? { + return Err(BoundlessError::Unauthorized); + } + + // Update status to Completed + campaign.status = Status::Completed; + + // Store updated campaign + env.storage() + .persistent() + .set(&DataKey::Campaign(campaign_id), &campaign); + + // Emit completion event + emit_campaign_completed_event(&env, campaign_id, admin); + Ok(()) } From 67ea0ac3d6978a6dafd61e56d65ca61a5915a7b8 Mon Sep 17 00:00:00 2001 From: Okoli Johnson <41733104+brisstone@users.noreply.github.com> Date: Tue, 7 Oct 2025 00:22:15 +0100 Subject: [PATCH 4/4] chore: expose helper --- contracts/boundless/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/contracts/boundless/src/lib.rs b/contracts/boundless/src/lib.rs index 962352d..6dc1102 100644 --- a/contracts/boundless/src/lib.rs +++ b/contracts/boundless/src/lib.rs @@ -5,6 +5,7 @@ use soroban_sdk::contract; mod datatypes; mod interface; mod logic; +mod helper; pub use logic::*;