Skip to content

milestones: no crowdfunding support — proportional refund-on-cancel is structurally harder here than in escrow due to partial allocation over time #58

Description

@chonilius

Overview

milestones::create_milestone has the identical single-sponsor limitation as escrow::fund: exactly one sponsor: Address parameter, one token_client.transfer(&sponsor, ..., &total_budget) call (contracts/milestones/src/lib.rs:54-87), and a permanent one-shot storage-key gate (contracts/milestones/src/lib.rs:67-70) that makes a second create_milestone call against the same milestone_id impossible regardless of who calls it. As with escrow, this means a release's total budget — potentially a substantial sum spanning many issues — can only ever come from a single sponsor's wallet, with no on-chain path for multiple sponsors (e.g. several companies jointly backing a release) to co-fund it.

Filed as a companion to, not a duplicate of, the escrow crowdfunding issue in this same batch, because the proportional-accounting problem here is structurally harder, not just "the same problem, twice":

  • Escrow's refund is a single event returning one fixed amount; a multi-sponsor escrow's refund logic just needs to iterate a flat contribution list once.
  • A milestone's budget, once deposited, gets allocated across potentially many issues over time (allocate, contracts/milestones/src/lib.rs:92-128) before any of it is ever paid out or refunded. Proportional accounting for multiple sponsors here means: at cancel_milestone time (or, once the timeout-escape-hatch issue filed elsewhere in this batch lands, at timeout-triggered wind-down), the remaining, unallocated budget must be returned to each contributing sponsor in proportion to what they contributed, not proportion to the original total_budget split — since if sponsor A contributed 70% and sponsor B contributed 30%, and only 40% of the total was ever allocated to issues (leaving 60% as remaining_budget), the refund needs to correctly attribute that 60% back as 70%/30% of the unspent portion, not naively split the nominal 60% figure some other way. This is meaningfully more involved than escrow's single-event refund, because it has to remain correct across an arbitrary number of intervening allocate/release_issue/(eventual)deallocate calls between the deposit and the eventual refund, not just at one fixed point in time.
  • It also interacts directly with the companion "no deallocate/reallocate" and "no timeout escape hatch" issues filed elsewhere in this batch — both of those issues' designs currently assume a single sponsor to refund to; a multi-sponsor milestone changes what "refund remaining_budget" even means at the exact point those other fixes would otherwise be straightforward.

Requirements

  • Design a multi-sponsor contribution ledger for milestones, structurally similar to what the escrow crowdfunding issue proposes, but explicitly accounting for the fact that remaining_budget shrinks over time via allocate (and potentially grows back via a future deallocate) between deposit and eventual refund.
  • Design proportional-to-contribution (not proportional-to-nominal-total) refund logic for cancel_milestone's (and any future timeout-triggered) refund of remaining_budget.
  • Explicitly decide the ordering/dependency between this issue and the "no deallocate/reallocate" and "no timeout escape hatch" issues filed elsewhere in this batch — implementing multi-sponsor support before those land is likely simpler (fewer interacting code paths to update at once); implementing it after means retrofitting proportional accounting into logic that was designed single-sponsor-only. This should be a conscious sequencing decision, stated in whichever PR lands first among the three.
  • Decide multi-sponsor allocate/admin-authorization implications: allocate/release_issue/cancel_milestone remain admin-only regardless (unaffected by how many sponsors funded the milestone), but any future sponsor-authorized action (e.g. a sponsor-triggered timeout recovery, per the companion escape-hatch issue) needs the same "which sponsor(s) get to act, and how" decision the escrow crowdfunding issue raises for extend_deadline.

Acceptance Criteria

  • Design decision documented for the contribution ledger and proportional-to-contribution refund accounting across allocate/deallocate cycles
  • Explicit sequencing decision relative to the deallocate/reallocate and timeout-escape-hatch issues, stated in the implementing PR
  • Implementation: milestones supports N sponsors co-funding one milestone_id, with correct proportional refund of remaining_budget at cancellation
  • Tests: multiple sponsors fund a milestone, some budget gets allocated and released to real issues, the milestone is cancelled, remaining budget is correctly returned to each sponsor in proportion to their contribution (not the nominal total)
  • cargo test --workspace passes

Additional Notes

  • Precise references: create_milestone's single-sponsor design at contracts/milestones/src/lib.rs:54-87; cancel_milestone's single-sponsor refund at contracts/milestones/src/lib.rs:201-209.
  • Test sketch: test_multi_sponsor_milestone_proportional_refund_after_partial_allocation — sponsor A deposits 700 (via a hypothetical multi-contribution create_milestone/contribute call), sponsor B deposits 300 (total 1000); allocate 400 to one issue, release_issue it; cancel_milestoneremaining_budget is 600 at this point; assert sponsor A receives 420 (70% of 600) and sponsor B receives 180 (30% of 600), not a 70/30 split of the original 1000 or an even split of the 600.
  • Cross-references: the companion escrow crowdfunding issue filed in this batch (the simpler variant of the same request, worth designing together for a consistent contribution-ledger shape reusable by both contracts, potentially via Extract compute_split into a shared crate with proof of behavioral equivalence #16's shared-crate effort); the "no deallocate/reallocate" issue and the "no timeout escape hatch" issue, both filed in this batch (interacting designs, as explained above — all three should be read together before any one of them lands).

Activity

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

Metadata

Metadata

Assignees

Labels

GrantFox OSSIssue tracked in GrantFox OSSMaybe RewardedIssue may be eligible for a GrantFox rewardOfficial Campaign | FWC26Campaign: Official Campaign | FWC26Third CampaignCampaign: Third CampaignarchitectureArchitecture/design issueenhancementNew feature or requestvery hardVery difficult task, expert-level effort required

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions