From adc5e5f9d930b3d2df933fb8192461300b0fb488 Mon Sep 17 00:00:00 2001 From: devjayy43 Date: Fri, 29 May 2026 07:27:54 +0100 Subject: [PATCH 1/2] fix: address issues #351, #352, #358, #359 - #358: remove first_creator block in contribute; after ownership transfer the original creator is a regular community member - #359: subtract only immediately-released funds from total_raised_global in withdraw_funds (keep reserve in the global), then subtract reserve_amount in withdraw_reserve when it is actually paid out - #351: add require_not_paused to set_vesting_params so vesting parameters cannot be changed silently while the contract is paused - #352: add require_not_paused to update_campaign and update_campaign_description to prevent metadata swaps during a pause --- src/lib.rs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index e5651cc..d6dcc3f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -366,7 +366,7 @@ impl ProofOfHeart { } require_active_campaign(&campaign)?; - if contributor == campaign.creator || contributor == campaign.first_creator { + if contributor == campaign.creator { return Err(Error::NotAuthorized); } if env.ledger().timestamp() > campaign.deadline { @@ -527,7 +527,7 @@ impl ProofOfHeart { set_total_raised_global( &env, total_raised - .checked_sub(campaign.amount_raised) + .checked_sub(campaign.amount_raised - reserve_amount) .ok_or(Error::Overflow)?, ); @@ -571,6 +571,14 @@ impl ProofOfHeart { reserve.released = true; set_campaign_reserve(&env, campaign_id, &reserve); + let total_raised = get_total_raised_global(&env); + set_total_raised_global( + &env, + total_raised + .checked_sub(reserve.amount) + .ok_or(Error::Overflow)?, + ); + env.events().publish( ("reserve_released", campaign_id, campaign.creator), reserve.amount, @@ -587,6 +595,7 @@ impl ProofOfHeart { reserve_bps: u32, ) -> Result<(), Error> { assert_admin(&env, &admin)?; + Self::require_not_paused(&env)?; if reserve_bps > 10000 || delay_days > 365 { return Err(Error::ValidationFailed); } @@ -665,6 +674,7 @@ impl ProofOfHeart { description: String, ) -> Result<(), Error> { let mut campaign = get_creator_campaign(&env, campaign_id)?; + Self::require_not_paused(&env)?; if campaign.amount_raised > 0 { return Err(Error::ValidationFailed); @@ -717,6 +727,7 @@ impl ProofOfHeart { description: String, ) -> Result<(), Error> { let mut campaign = get_creator_campaign(&env, campaign_id)?; + Self::require_not_paused(&env)?; require_active_campaign(&campaign)?; if description.len() < CAMPAIGN_DESCRIPTION_MIN_LEN From f9938eb309ff09077f7e06791d5ba2e8f537af62 Mon Sep 17 00:00:00 2001 From: devjayy43 Date: Fri, 29 May 2026 07:36:57 +0100 Subject: [PATCH 2/2] test: update campaign transfer contribute test for issue #358 Replace original_creator_cannot_contribute_after_campaign_transfer with original_creator_can_contribute_after_campaign_transfer to reflect the corrected behaviour: after ownership transfer the original creator is a regular community member and must be allowed to contribute. --- src/campaign_transfer_test.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/campaign_transfer_test.rs b/src/campaign_transfer_test.rs index fe62fb6..5a04445 100644 --- a/src/campaign_transfer_test.rs +++ b/src/campaign_transfer_test.rs @@ -1,4 +1,5 @@ use super::*; +use soroban_sdk::token::StellarAssetClient as TokenAdminClient; use soroban_sdk::{testutils::Address as _, Address, Env, String}; fn setup_env<'a>() -> (Env, Address, Address, ProofOfHeartClient<'a>) { @@ -90,17 +91,22 @@ fn campaign_transfer_cancel_then_reinitiate_succeeds() { } #[test] -fn original_creator_cannot_contribute_after_campaign_transfer() { +fn original_creator_can_contribute_after_campaign_transfer() { let (env, _admin, creator, client) = setup_env(); let new_creator = Address::generate(&env); let campaign_id = create_campaign(&env, &client, &creator, "Transfer contribution guard"); + let token_admin = TokenAdminClient::new(&env, &client.get_token()); + token_admin.mint(&creator, &100); + client.verify_campaign(&campaign_id); client.initiate_campaign_transfer(&campaign_id, &new_creator); client.accept_campaign_transfer(&campaign_id); + // After transfer, the original creator is a regular community member and + // must be allowed to contribute. let res = client.try_contribute(&campaign_id, &creator, &100); - assert_eq!(res.unwrap_err().unwrap(), Error::NotAuthorized); + assert!(res.is_ok()); } #[test]