From a5cb9036b1d25e5aea27e648a63536b75af5ca1c Mon Sep 17 00:00:00 2001 From: LaGodxy Date: Fri, 6 Mar 2026 15:25:17 -0800 Subject: [PATCH 1/3] feat: implement core Soroban campaign management contract --- contracts/campaign/src/lib.rs | 177 ++++++++++++++++++++++++++++------ 1 file changed, 150 insertions(+), 27 deletions(-) diff --git a/contracts/campaign/src/lib.rs b/contracts/campaign/src/lib.rs index 68b4457..f664167 100644 --- a/contracts/campaign/src/lib.rs +++ b/contracts/campaign/src/lib.rs @@ -1,39 +1,162 @@ #![no_std] -use soroban_sdk::{Address, Env, Symbol, contract, contractimpl}; +use soroban_sdk::{ + Address, Env, Symbol, contract, contractimpl, + Map, Vec, symbol_short +}; + +// Storage keys +const CAMPAIGN_MAP: Symbol = symbol_short!("CMP_MAP"); +const CAMPAIGN_COUNT: Symbol = symbol_short!("CMP_CNT"); + +// Campaign status constants +pub const CAMPAIGN_STATUS_ACTIVE: u32 = 0; +pub const CAMPAIGN_STATUS_COMPLETED: u32 = 1; +pub const CAMPAIGN_STATUS_CANCELLED: u32 = 2; +pub const CAMPAIGN_STATUS_EXPIRED: u32 = 3; + +// Campaign data tuple: (id, owner, goal, deadline, status, created_at) +pub type Campaign = (u64, Address, i128, u64, u32, u64); + +// Events - use simple tuples for compatibility +pub type CampaignRegisteredEvent = (u64, Address, i128, u64); // (campaign_id, owner, goal, deadline) +pub type CampaignStatusUpdatedEvent = (u64, u32, u32); // (campaign_id, old_status, new_status) #[contract] pub struct CampaignContract; #[contractimpl] impl CampaignContract { - /// Create a new campaign - pub fn create(env: Env, campaign_id: Symbol, title: Symbol, target: i128, deadline: u64) { - let key = Symbol::new(&env, "campaign_data"); - env.storage() - .instance() - .set(&key, &(campaign_id, title, target, deadline)); + /// Register a new campaign + /// + /// # Arguments + /// * `env` - The contract environment + /// * `owner` - The address of campaign owner + /// * `goal` - The funding goal for campaign + /// * `deadline` - The deadline timestamp for campaign + /// + /// # Returns + /// The ID of newly created campaign + pub fn register_campaign(env: Env, owner: Address, goal: i128, deadline: u64) -> u64 { + owner.require_auth(); + + // Get current campaign count and increment + let mut count: u64 = env.storage().instance().get(&CAMPAIGN_COUNT).unwrap_or(0); + count += 1; + + // Create new campaign tuple: (id, owner, goal, deadline, status, created_at) + let campaign: Campaign = ( + count, + owner.clone(), + goal, + deadline, + CAMPAIGN_STATUS_ACTIVE, + env.ledger().timestamp(), + ); + + // Store campaign in map + let mut campaigns: Map = env.storage().instance().get(&CAMPAIGN_MAP).unwrap_or(Map::new(&env)); + campaigns.set(count, campaign); + env.storage().instance().set(&CAMPAIGN_MAP, &campaigns); + + // Update campaign count + env.storage().instance().set(&CAMPAIGN_COUNT, &count); + + // Emit event + env.events().publish( + (Symbol::new(&env, "CampaignRegistered"), count), + (count, owner, goal, deadline) as CampaignRegisteredEvent, + ); + + count } - - /// Get campaign status - pub fn get_status(env: Env) -> (Symbol, Symbol, i128, u64) { - let key = Symbol::new(&env, "campaign_data"); - env.storage() - .instance() - .get(&key) - .expect("campaign not initialized") + + /// Get campaign details by ID + /// + /// # Arguments + /// * `env` - The contract environment + /// * `campaign_id` - The ID of campaign to retrieve + /// + /// # Returns + /// The Campaign tuple if found + pub fn get_campaign(env: Env, campaign_id: u64) -> Campaign { + let campaigns: Map = env.storage().instance().get(&CAMPAIGN_MAP) + .unwrap_or_else(|| panic!("No campaigns found")); + + campaigns.get(campaign_id) + .unwrap_or_else(|| panic!("Campaign not found")) } - - /// Check if campaign is active - pub fn is_active(env: Env) -> bool { - let key = Symbol::new(&env, "campaign_data"); - let (_id, _title, _target, deadline): (Symbol, Symbol, i128, u64) = env - .storage() - .instance() - .get(&key) - .expect("campaign not initialized"); - - let current_time = env.ledger().timestamp(); - current_time < deadline + + /// Update campaign status + /// + /// # Arguments + /// * `env` - The contract environment + /// * `campaign_id` - The ID of campaign to update + /// * `status` - The new status for campaign + pub fn update_campaign_status(env: Env, campaign_id: u64, status: u32) { + let mut campaigns: Map = env.storage().instance().get(&CAMPAIGN_MAP) + .unwrap_or_else(|| panic!("No campaigns found")); + + let campaign = campaigns.get(campaign_id) + .unwrap_or_else(|| panic!("Campaign not found")); + + // Extract campaign data + let (id, owner, goal, deadline, old_status, created_at) = campaign; + + // Only campaign owner can update status + owner.require_auth(); + + // Create updated campaign tuple + let updated_campaign: Campaign = ( + id, + owner, + goal, + deadline, + status as u32, + created_at, + ); + + campaigns.set(campaign_id, updated_campaign); + env.storage().instance().set(&CAMPAIGN_MAP, &campaigns); + + // Emit event + env.events().publish( + (Symbol::new(&env, "CampaignStatusUpdated"), campaign_id), + (campaign_id, old_status, status) as CampaignStatusUpdatedEvent, + ); + } + + /// Get total number of campaigns + /// + /// # Arguments + /// * `env` - The contract environment + /// + /// # Returns + /// The total count of registered campaigns + pub fn get_campaign_count(env: Env) -> u64 { + env.storage().instance().get(&CAMPAIGN_COUNT).unwrap_or(0) + } + + /// Get all campaigns (utility function for testing) + /// + /// # Arguments + /// * `env` - The contract environment + /// + /// # Returns + /// Vector of all campaigns + pub fn get_all_campaigns(env: Env) -> Vec { + let campaigns: Map = env.storage().instance().get(&CAMPAIGN_MAP) + .unwrap_or_else(|| Map::new(&env)); + + let mut result = Vec::new(&env); + let keys = campaigns.keys(); + + for key in keys { + if let Some(campaign) = campaigns.get(key) { + result.push_back(campaign); + } + } + + result } } From 0fe87ee816fe7b5695f0ee8827dd522c4f03dda8 Mon Sep 17 00:00:00 2001 From: LaGodxy Date: Fri, 6 Mar 2026 15:33:49 -0800 Subject: [PATCH 2/3] fix: ensure GitHub Actions workflow compatibility --- contracts/campaign/src/lib.rs | 103 ++++++++++++++++++---------------- 1 file changed, 54 insertions(+), 49 deletions(-) diff --git a/contracts/campaign/src/lib.rs b/contracts/campaign/src/lib.rs index f664167..e69c22c 100644 --- a/contracts/campaign/src/lib.rs +++ b/contracts/campaign/src/lib.rs @@ -1,9 +1,6 @@ #![no_std] -use soroban_sdk::{ - Address, Env, Symbol, contract, contractimpl, - Map, Vec, symbol_short -}; +use soroban_sdk::{Address, Env, Map, Symbol, Vec, contract, contractimpl, symbol_short}; // Storage keys const CAMPAIGN_MAP: Symbol = symbol_short!("CMP_MAP"); @@ -19,8 +16,8 @@ pub const CAMPAIGN_STATUS_EXPIRED: u32 = 3; pub type Campaign = (u64, Address, i128, u64, u32, u64); // Events - use simple tuples for compatibility -pub type CampaignRegisteredEvent = (u64, Address, i128, u64); // (campaign_id, owner, goal, deadline) -pub type CampaignStatusUpdatedEvent = (u64, u32, u32); // (campaign_id, old_status, new_status) +pub type CampaignRegisteredEvent = (u64, Address, i128, u64); +pub type CampaignStatusUpdatedEvent = (u64, u32, u32); #[contract] pub struct CampaignContract; @@ -28,22 +25,22 @@ pub struct CampaignContract; #[contractimpl] impl CampaignContract { /// Register a new campaign - /// + /// /// # Arguments /// * `env` - The contract environment /// * `owner` - The address of campaign owner /// * `goal` - The funding goal for campaign /// * `deadline` - The deadline timestamp for campaign - /// + /// /// # Returns /// The ID of newly created campaign pub fn register_campaign(env: Env, owner: Address, goal: i128, deadline: u64) -> u64 { owner.require_auth(); - + // Get current campaign count and increment let mut count: u64 = env.storage().instance().get(&CAMPAIGN_COUNT).unwrap_or(0); count += 1; - + // Create new campaign tuple: (id, owner, goal, deadline, status, created_at) let campaign: Campaign = ( count, @@ -53,110 +50,118 @@ impl CampaignContract { CAMPAIGN_STATUS_ACTIVE, env.ledger().timestamp(), ); - + // Store campaign in map - let mut campaigns: Map = env.storage().instance().get(&CAMPAIGN_MAP).unwrap_or(Map::new(&env)); + let mut campaigns: Map = env + .storage() + .instance() + .get(&CAMPAIGN_MAP) + .unwrap_or(Map::new(&env)); campaigns.set(count, campaign); env.storage().instance().set(&CAMPAIGN_MAP, &campaigns); - + // Update campaign count env.storage().instance().set(&CAMPAIGN_COUNT, &count); - + // Emit event env.events().publish( (Symbol::new(&env, "CampaignRegistered"), count), (count, owner, goal, deadline) as CampaignRegisteredEvent, ); - + count } - + /// Get campaign details by ID - /// + /// /// # Arguments /// * `env` - The contract environment /// * `campaign_id` - The ID of campaign to retrieve - /// + /// /// # Returns /// The Campaign tuple if found pub fn get_campaign(env: Env, campaign_id: u64) -> Campaign { - let campaigns: Map = env.storage().instance().get(&CAMPAIGN_MAP) + let campaigns: Map = env + .storage() + .instance() + .get(&CAMPAIGN_MAP) .unwrap_or_else(|| panic!("No campaigns found")); - - campaigns.get(campaign_id) + + campaigns + .get(campaign_id) .unwrap_or_else(|| panic!("Campaign not found")) } - + /// Update campaign status - /// + /// /// # Arguments /// * `env` - The contract environment /// * `campaign_id` - The ID of campaign to update /// * `status` - The new status for campaign pub fn update_campaign_status(env: Env, campaign_id: u64, status: u32) { - let mut campaigns: Map = env.storage().instance().get(&CAMPAIGN_MAP) + let mut campaigns: Map = env + .storage() + .instance() + .get(&CAMPAIGN_MAP) .unwrap_or_else(|| panic!("No campaigns found")); - - let campaign = campaigns.get(campaign_id) + + let campaign = campaigns + .get(campaign_id) .unwrap_or_else(|| panic!("Campaign not found")); - + // Extract campaign data let (id, owner, goal, deadline, old_status, created_at) = campaign; - + // Only campaign owner can update status owner.require_auth(); - + // Create updated campaign tuple - let updated_campaign: Campaign = ( - id, - owner, - goal, - deadline, - status as u32, - created_at, - ); - + let updated_campaign: Campaign = (id, owner, goal, deadline, status as u32, created_at); + campaigns.set(campaign_id, updated_campaign); env.storage().instance().set(&CAMPAIGN_MAP, &campaigns); - + // Emit event env.events().publish( (Symbol::new(&env, "CampaignStatusUpdated"), campaign_id), (campaign_id, old_status, status) as CampaignStatusUpdatedEvent, ); } - + /// Get total number of campaigns - /// + /// /// # Arguments /// * `env` - The contract environment - /// + /// /// # Returns /// The total count of registered campaigns pub fn get_campaign_count(env: Env) -> u64 { env.storage().instance().get(&CAMPAIGN_COUNT).unwrap_or(0) } - + /// Get all campaigns (utility function for testing) - /// + /// /// # Arguments /// * `env` - The contract environment - /// + /// /// # Returns /// Vector of all campaigns pub fn get_all_campaigns(env: Env) -> Vec { - let campaigns: Map = env.storage().instance().get(&CAMPAIGN_MAP) + let campaigns: Map = env + .storage() + .instance() + .get(&CAMPAIGN_MAP) .unwrap_or_else(|| Map::new(&env)); - + let mut result = Vec::new(&env); let keys = campaigns.keys(); - + for key in keys { if let Some(campaign) = campaigns.get(key) { result.push_back(campaign); } } - + result } } From dd63a7e9e4f988a8299959170a58a7c15ff15d20 Mon Sep 17 00:00:00 2001 From: LaGodxy Date: Fri, 6 Mar 2026 15:37:24 -0800 Subject: [PATCH 3/3] fix: resolve formatting issue in campaign contract --- contracts/campaign/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/campaign/src/lib.rs b/contracts/campaign/src/lib.rs index e69c22c..eaae5c2 100644 --- a/contracts/campaign/src/lib.rs +++ b/contracts/campaign/src/lib.rs @@ -16,8 +16,8 @@ pub const CAMPAIGN_STATUS_EXPIRED: u32 = 3; pub type Campaign = (u64, Address, i128, u64, u32, u64); // Events - use simple tuples for compatibility -pub type CampaignRegisteredEvent = (u64, Address, i128, u64); -pub type CampaignStatusUpdatedEvent = (u64, u32, u32); +pub type CampaignRegisteredEvent = (u64, Address, i128, u64); // (campaign_id, owner, goal, deadline) +pub type CampaignStatusUpdatedEvent = (u64, u32, u32); // (campaign_id, old_status, new_status) #[contract] pub struct CampaignContract;