@@ -4,7 +4,7 @@ pub mod storage;
44pub mod types;
55
66use soroban_sdk:: { contract, contractimpl, Env , Vec } ;
7- use types:: { AssetInfo , CampaignData , CampaignStatus , Error , MilestoneData , MilestoneStatus } ;
7+ use types:: { CampaignData , CampaignStatus , Error , MilestoneData , MilestoneStatus , StellarAsset , CampaignEvent } ;
88use storage:: { get_campaign, set_campaign, set_milestone} ;
99
1010pub const VERSION : u32 = 1 ;
@@ -16,20 +16,38 @@ pub struct CampaignContract;
1616impl CampaignContract {
1717 /// Initialize a new campaign with strict validation on all inputs.
1818 ///
19+ /// Requires: Creator authorization via `creator.require_auth()`
20+ /// Can only be called once per contract instance
21+ ///
1922 /// # Panics
23+ /// - `Error::UnauthorizedCreator` if caller is not the creator or lacks authorization
24+ /// - `Error::AlreadyInitialized` if campaign already exists
2025 /// - `Error::InvalidGoalAmount` if goal_amount <= 0
2126 /// - `Error::InvalidEndTime` if end_time <= current ledger timestamp
2227 /// - `Error::InvalidAssets` if accepted_assets is empty
28+ /// - `Error::InvalidAssetCode` if any asset_code is empty or invalid
29+ /// - `Error::InvalidMilestoneCount` if milestone count is not 1-5
2330 /// - `Error::InvalidMilestones` if milestones are not sorted ascending by target_amount
2431 /// - `Error::MilestoneMismatch` if last milestone.target_amount != goal_amount
32+ ///
33+ /// # Events
34+ /// Emits `campaign_initialized` event with campaign details
2535 pub fn initialize (
2636 env : Env ,
2737 creator : soroban_sdk:: Address ,
2838 goal_amount : i128 ,
2939 end_time : u64 ,
30- accepted_assets : Vec < AssetInfo > ,
40+ accepted_assets : Vec < StellarAsset > ,
3141 milestones : Vec < MilestoneData > ,
3242 ) -> Result < ( ) , Error > {
43+ // Authorization check: creator must authorize this call
44+ creator. require_auth ( ) ;
45+
46+ // Check if already initialized - can only initialize once
47+ if get_campaign ( & env) . is_some ( ) {
48+ panic_with_error ( & env, Error :: AlreadyInitialized ) ;
49+ }
50+
3351 // Validation 1: goal_amount > 0
3452 if goal_amount <= 0 {
3553 panic_with_error ( & env, Error :: InvalidGoalAmount ) ;
@@ -46,20 +64,27 @@ impl CampaignContract {
4664 panic_with_error ( & env, Error :: InvalidAssets ) ;
4765 }
4866
49- // Validation 4 & 5: milestones sorted ascending and last == goal_amount
50- if !milestones. is_empty ( ) {
51- validate_milestones ( & env, & milestones, goal_amount) ?;
67+ // Validation 3b: validate each asset code
68+ validate_assets ( & env, & accepted_assets) ?;
69+
70+ // Validation 4: milestone count must be 1-5
71+ let milestone_count = milestones. len ( ) as u32 ;
72+ if milestone_count == 0 || milestone_count > types:: MAX_MILESTONES {
73+ panic_with_error ( & env, Error :: InvalidMilestoneCount ) ;
5274 }
5375
76+ // Validation 5 & 6: milestones sorted ascending and last == goal_amount
77+ validate_milestones ( & env, & milestones, goal_amount) ?;
78+
5479 // All validations passed, store campaign data
5580 let campaign = CampaignData {
56- creator,
81+ creator : creator . clone ( ) ,
5782 goal_amount,
5883 raised_amount : 0 ,
5984 end_time,
6085 status : CampaignStatus :: Active ,
61- accepted_assets,
62- milestone_count : milestones . len ( ) as u32 ,
86+ accepted_assets : accepted_assets . clone ( ) ,
87+ milestone_count,
6388 } ;
6489
6590 set_campaign ( & env, & campaign) ;
@@ -69,6 +94,16 @@ impl CampaignContract {
6994 set_milestone ( & env, index as u32 , milestone) ;
7095 }
7196
97+ // Emit campaign_initialized event
98+ let event = CampaignEvent :: Initialized {
99+ creator,
100+ goal_amount,
101+ end_time,
102+ asset_count : accepted_assets. len ( ) as u32 ,
103+ milestone_count,
104+ } ;
105+ env. events ( ) . publish ( ( "campaign" , "initialized" ) , event) ;
106+
72107 Ok ( ( ) )
73108 }
74109
@@ -81,6 +116,18 @@ impl CampaignContract {
81116 }
82117}
83118
119+ /// Helper function to validate Stellar assets
120+ /// Ensures each asset has a non-empty asset_code
121+ fn validate_assets ( env : & Env , assets : & Vec < StellarAsset > ) -> Result < ( ) , Error > {
122+ for asset in assets. iter ( ) {
123+ // asset_code must be non-empty
124+ if asset. asset_code . len ( ) == 0 {
125+ panic_with_error ( env, Error :: InvalidAssetCode ) ;
126+ }
127+ }
128+ Ok ( ( ) )
129+ }
130+
84131/// Helper function to validate milestone conditions
85132fn validate_milestones (
86133 env : & Env ,
@@ -115,8 +162,12 @@ fn panic_with_error(env: &Env, error: Error) -> ! {
115162 Error :: InvalidGoalAmount => "InvalidGoalAmount" ,
116163 Error :: InvalidEndTime => "InvalidEndTime" ,
117164 Error :: InvalidAssets => "InvalidAssets" ,
165+ Error :: InvalidAssetCode => "InvalidAssetCode" ,
118166 Error :: InvalidMilestones => "InvalidMilestones" ,
119167 Error :: MilestoneMismatch => "MilestoneMismatch" ,
168+ Error :: InvalidMilestoneCount => "InvalidMilestoneCount" ,
169+ Error :: AlreadyInitialized => "AlreadyInitialized" ,
170+ Error :: UnauthorizedCreator => "UnauthorizedCreator" ,
120171 Error :: InvalidCampaignTransition => "InvalidCampaignTransition" ,
121172 Error :: InvalidMilestoneTransition => "InvalidMilestoneTransition" ,
122173 Error :: CampaignNotActive => "CampaignNotActive" ,
0 commit comments