diff --git a/campaign/src/lib.rs b/campaign/src/lib.rs index a83ffc7..ad8079e 100644 --- a/campaign/src/lib.rs +++ b/campaign/src/lib.rs @@ -33,6 +33,7 @@ use storage::{ storage_increment_donation_count, storage_increment_unique_donor_count, storage_set_total_raised, }; + use types::{ AssetInfo, CampaignData, CampaignInitializedEvent, CampaignReport, CampaignStatus, CampaignStatusResponse, DashboardMetrics, DonorRecord, Error, MilestoneData, MilestoneStatus, @@ -210,6 +211,8 @@ impl CampaignContract { storage_increment_asset_raised(&env, &asset_address, amount); increment_donor_asset_donation(&env, &donor, &asset_address, amount); + let _donor_record = + get_donor(&env, &donor).unwrap_or(DonorRecord::new_for(donor.clone(), asset.clone())); // Update donor record let existing_donor = get_donor(&env, &donor); let is_new_donor = existing_donor.is_none(); @@ -395,6 +398,9 @@ impl CampaignContract { let campaign = get_campaign(&env).unwrap_or_else(|| panic_with_error(&env, Error::NotInitialized)); + let _donor_record = + get_donor(&env, &donor).unwrap_or_else(|| panic_with_error(&env, Error::NoDonorRecord)); + let mut donor_record = get_donor(&env, &donor).unwrap_or_else(|| panic_with_error(&env, Error::NoDonorRecord)); @@ -615,6 +621,17 @@ impl CampaignContract { } } +/// Issue #175 – assert the current invoker is the campaign creator. +/// +/// Reads the creator address from campaign storage and calls `require_auth()`. +/// Panics with `Error::Unauthorized` if the campaign is not initialized; +/// Soroban's auth framework panics if the invoker is not the creator. +#[allow(dead_code)] +fn require_creator(env: &Env) { + let campaign = get_campaign(env).unwrap_or_else(|| panic_with_error(env, Error::Unauthorized)); + campaign.creator.require_auth(); +} + /// Validates that `asset` is in the campaign's accepted list and returns the /// token contract address needed to construct a `token::Client`. fn get_token_address_for_asset(env: &Env, asset: &AssetInfo, campaign: &CampaignData) -> Address { diff --git a/campaign/src/types.rs b/campaign/src/types.rs index c70eac7..a98d4d4 100644 --- a/campaign/src/types.rs +++ b/campaign/src/types.rs @@ -1,6 +1,8 @@ // src/types.rs -use soroban_sdk::{contracterror, contracttype, Address, BytesN, Env, String, Vec}; +use soroban_sdk::{ + contracterror, contracttype, panic_with_error, Address, BytesN, Env, String, Vec, +}; // ─── Error enum ─────────────────────────────────────────────────────────────── @@ -450,6 +452,9 @@ impl DonorRecord { } } + /// Apply a new donation to this record. Returns an error string (for + /// debug builds) rather than panicking so the call site can choose how + /// to surface it. /// Apply a new donation to this record. Panics with `Error::Overflow` if /// `total_donated` or `donation_count` overflows. pub fn apply_donation( @@ -463,13 +468,13 @@ impl DonorRecord { self.total_donated = self .total_donated .checked_add(amount) - .unwrap_or_else(|| env.panic_with_error(Error::Overflow)); + .unwrap_or_else(|| panic_with_error!(&env, Error::Overflow)); self.last_donation_time = time; self.last_donation_ledger = ledger; self.donation_count = self .donation_count .checked_add(1) - .unwrap_or_else(|| env.panic_with_error(Error::Overflow)); + .unwrap_or_else(|| panic_with_error!(&env, Error::Overflow)); self.asset = asset; } }