Skip to content

milestones: block release_issue on a closed milestone - #231

Open
gideononiru wants to merge 2 commits into
MergeFi:mainfrom
gideononiru:fix/issue-5-release-issue-blocks-closed-milestone
Open

milestones: block release_issue on a closed milestone#231
gideononiru wants to merge 2 commits into
MergeFi:mainfrom
gideononiru:fix/issue-5-release-issue-blocks-closed-milestone

Conversation

@gideononiru

Copy link
Copy Markdown

Summary

The other three issues in this batch (#3 non-standard-token accounting, #17 property-based fuzzing, #20 mutable fee_bps) are genuine multi-day-scale design-and-implementation efforts on a live financial contract — I'm continuing to work through them and will follow up with additional PRs rather than rush a financial-accounting change I haven't fully verified. Opening this one now since it's a complete, verified, self-contained fix.

The bug

cancel_milestone/cancel_milestone_after_deadline only refund remaining_budget — the unallocated portion of a milestone's pool — back to the sponsor. Amounts already reserved via allocate() live in milestone.allocations and are deliberately excluded from that refund. release_issue checked IssueStatus (Allocated/Released) but never checked milestone.closed, so an issue left in IssueStatus::Allocated when the milestone closed could still be released afterward — paying out real token amounts the sponsor's refund had already implicitly treated as settled.

Error::MilestoneClosed already existed and was already used by contribute/allocate/cancel_milestone/cancel_milestone_after_deadlinerelease_issue was the one call site missing it.

Fix

Added the milestone.closed check to release_issue, as the first thing it does after loading the milestone record, before touching IssueStatus or moving any funds. This doesn't strand the allocated amount: deallocate() (already present in this codebase) is the admin's documented path to reclaim an Allocated, not-yet-Released issue's amount back into remaining_budget before cancelling.

Test plan

  • New regression test: allocate an issue, cancel the milestone (only the unallocated remainder is refunded), assert release_issue on the still-Allocated issue is rejected with MilestoneClosed and the allocated amount stays untouched in the contract.
  • cargo test -p mergefi-milestones --lib — 41/41 passing (1 new), no regressions.

Closes MergeFi#5

cancel_milestone / cancel_milestone_after_deadline only refund
remaining_budget (the unallocated portion) to the sponsor — amounts
already reserved via allocate() live in milestone.allocations and are
not part of that refund. release_issue checked IssueStatus
(Allocated/Released) but never checked milestone.closed, so an issue
left in IssueStatus::Allocated when the milestone closed could still
be released afterward: real token amounts the sponsor's refund had
already implicitly treated as settled would leave the contract on a
later release_issue call.

Error::MilestoneClosed already existed and is already used by
contribute/allocate/cancel_milestone/cancel_milestone_after_deadline
— release_issue was the one call site missing it. Added the same
check as the very first thing release_issue does after loading the
milestone, before touching IssueStatus or moving any funds.

This doesn't strand the allocated amount: deallocate() (added
alongside this fix) is the admin's path to reclaim an Allocated,
not-yet-Released issue's amount back into remaining_budget — either
before cancelling (recommended) or, if an issue was left allocated
through a cancel, after the fact via deallocate + a fresh
create_milestone/contribute cycle if the funds still need to reach
their intended recipient.
Part of MergeFi#5

Regression test for the fix in the previous commit: allocates an
issue, cancels the milestone (only the unallocated remainder is
refunded to the sponsor), then asserts release_issue on the still-
Allocated issue is rejected with MilestoneClosed and the allocated
amount stays untouched in the contract (not paid to the maintainer,
not silently lost) rather than being released post-cancellation.
@vercel

vercel Bot commented Aug 30, 2026

Copy link
Copy Markdown

@gideononiru is attempting to deploy a commit to the chonilius' projects Team on Vercel.

A member of the Team first needs to authorize it.

@drips-wave

drips-wave Bot commented Aug 30, 2026

Copy link
Copy Markdown

@gideononiru 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

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.

cancel_milestone does not block release_issue for already-allocated, not-yet-released issues — budget/accounting invariant violation

1 participant