Skip to content
Open
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
11 changes: 11 additions & 0 deletions contracts/boundless/src/datatypes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand All @@ -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,
}
24 changes: 24 additions & 0 deletions contracts/boundless/src/helper.rs
Original file line number Diff line number Diff line change
@@ -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<bool, BoundlessError> {
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);
}
1 change: 1 addition & 0 deletions contracts/boundless/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use soroban_sdk::contract;
mod datatypes;
mod interface;
mod logic;
mod helper;

pub use logic::*;

Expand Down
44 changes: 37 additions & 7 deletions contracts/boundless/src/logic/campaign.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand Down Expand Up @@ -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(())
}

Expand Down
Loading