Skip to content

fix: cancel_campaign must decrement total_raised_global upfront (#439) - #615

Closed
baedboibidex-cmyk wants to merge 14 commits into
Iris-IV:mainfrom
baedboibidex-cmyk:fix/cancel_campaign_decrement_total_raised_global
Closed

fix: cancel_campaign must decrement total_raised_global upfront (#439)#615
baedboibidex-cmyk wants to merge 14 commits into
Iris-IV:mainfrom
baedboibidex-cmyk:fix/cancel_campaign_decrement_total_raised_global

Conversation

@baedboibidex-cmyk

@baedboibidex-cmyk baedboibidex-cmyk commented Jul 29, 2026

Copy link
Copy Markdown

closes #439
Decrement total_raised_global immediately in cancel_campaign and admin_cancel_campaign so cancelled campaigns with unclaimed refunds no longer inflate global stats.

Also introduces PendingRefundTotal storage counter to track unclaimed refunds across cancelled campaigns, so accept_token_update can reliably block when escrowed refunds remain (even after total_raised_global is zeroed at cancel time).

Changes:

  • Add PendingRefundTotal to AdminKey storage (track unclaimed refunds on cancelled campaigns)
  • Subtract campaign.amount_raised from total_raised_global upfront in cancel_campaign and admin_cancel_campaign
  • Increment pending_refund_total at cancel time
  • In claim_refund, decrement pending_refund_total for cancelled campaigns (skip total_raised_global to avoid double-decrement)
  • In accept_token_update, gate on pending_refund_total == 0 in addition to total_raised_global == 0
  • Initialize pending_refund_total to 0 in init()

Co-authored-by: Promise Olubudo 254755326+baedboibidex-cmyk@users.noreply.github.com

…l_campaign

Track unclaimed refunds across cancelled campaigns via PendingRefundTotal,
so accept_token_update can gate on both zero raised and zero pending refunds.

- Add PendingRefundTotal to AdminKey storage
- Subtract campaign.amount_raised from total_raised_global at cancel time
- Increment pending_refund_total at cancel time
- Decrement pending_refund_total in claim_refund for cancelled campaigns
- Update accept_token_update gate to also check pending_refund_total == 0
- Update claim_refund to skip total_raised_global decrement for cancelled
  campaigns (already decremented at cancel)

Co-authored-by: Promise Olubudo <254755326+baedboibidex-cmyk@users.noreply.github.com>
@drips-wave

drips-wave Bot commented Jul 29, 2026

Copy link
Copy Markdown

@baedboibidex-cmyk Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

victor-134 and others added 11 commits July 30, 2026 09:36
…ransfer (Iris-IV#453)

Prevents users from wasting a Freighter signature when the contract
is paused. The paused check now happens before pending.require_auth().
…ed payload

The update_campaign function now emits (old_title, old_description, title,
event_description) as a 4-tuple, but the tests were still unpacking as a
2-tuple (String, String), causing HostError(UnexpectedSize).

Updated test_update_campaign_emits_title_and_description and
test_update_campaign_event_tracks_latest_description to unpack the 4-tuple
and assert against the correct indices (2 and 3 for new values).
…sonal_cap (Iris-IV#503)

Reverts the broken duplicate contract and stray files shipped by two bad
merges, and properly re-implements the feature they attempted:

- Remove the stray `#[contract] ProofOfHeartContract` duplicate contract
  (list_active_campaigns with tag_filter, category max-goal caps) from
  lib.rs/admin.rs, leaving the real ProofOfHeart contract as the only one.
- Delete orphaned files from Iris-IV#616/Iris-IV#618 that referenced the removed contract
  or were stray: src/proof_of_heart/, src/tests/voting_tests.rs,
  src/events.ts, src/types.ts, src/campaigns.rs, src/clients.rs, src/test.rs,
  and the frontend/ directory (no build setup; README references a separate
  frontend repo).
- Re-implement issue Iris-IV#503 properly on the real contract: public
  remove_personal_cap(campaign_id, contributor) entrypoint that removes a
  contributor's personal contribution cap (requires contributor auth and an
  active campaign), new Error::PersonalCapNotFound (46), and a
  personal_cap_removed event.
- Update docs: AUTHORIZATION.md, EVENT_PAYLOADS.md (count 49), CHANGELOG.md,
  and merge the duplicated sections of CAMPAIGN_LIFECYCLE.md into one
  coherent document (DataKey -> AdminKey renames).
- Add tests covering restore/remove flow, event shape, not-found and
  inactive-campaign errors, and contributor auth recording.

All 405 tests pass; cargo check, fmt, clippy clean.

@davidmaronio davidmaronio left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the core design here is sound and thoughtful: decrementing total_raised_global upfront in cancel_campaign / admin_cancel_campaign (src/campaigns/cancel.rs), moving the escrow into a new PendingRefundTotal counter, decrementing that per-claimant in claim_refund (src/contributions.rs:285), and widening the accept_token_update gate in src/admin.rs:365 to include the pending refund total keeps the token-swap guard honest without double-counting. updating the get_platform_stats expectation with a comment explaining why is also the right move.

things to fix:

  1. scope: the diff carries a lot that has nothing to do with #439 - the whole remove_personal_cap entrypoint (#503) with its tests, error variant, CHANGELOG/EVENT_PAYLOADS/AUTHORIZATION doc entries, the duplicate-contract revert (src/proof_of_heart deletions, voting_tests.rs, frontend file deletions), and the accept_campaign_transfer pause reorder. some of this may be base drift since the branch is conflicted, but as it stands the PR is unreviewable as one unit. please rebase onto current main so the diff is only the #439 change.
  2. migration hazard worth addressing (or at least documenting): a campaign cancelled before this change deploys has its escrow still counted in total_raised_global and nothing in PendingRefundTotal. after upgrade, claim_refund for that campaign takes the is_cancelled branch and does pending_refund.checked_sub(amount) on a zero counter, which returns Err(Overflow)... actually checked_sub on i128 only errors on true overflow, so it would drive PendingRefundTotal negative and leave total_raised_global permanently inflated, blocking token swaps forever. a migrate step or a floor-at-zero with an explicit comment is needed.
  3. src/campaigns/cancel.rs - the same two-counter block is pasted into both cancel_campaign and admin_cancel_campaign. pull it into a small helper so the two paths can't drift.
  4. overlap: #714, #604 and #718 touch the same accounting. worth agreeing with the maintainers which approach wins before polishing further.

gate: this branch has merge conflicts with main. please resolve them (which should also shed the unrelated changes), address point 2, and push.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] cancel_campaign does not decrement total_raised_global upfront — global stat inflated until every contributor claims refund

3 participants