You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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_milestone — remaining_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).
Overview
milestones::create_milestonehas the identical single-sponsor limitation asescrow::fund: exactly onesponsor: Addressparameter, onetoken_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 secondcreate_milestonecall against the samemilestone_idimpossible 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":
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: atcancel_milestonetime (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% asremaining_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 interveningallocate/release_issue/(eventual)deallocatecalls between the deposit and the eventual refund, not just at one fixed point in time.remaining_budget" even means at the exact point those other fixes would otherwise be straightforward.Requirements
milestones, structurally similar to what the escrow crowdfunding issue proposes, but explicitly accounting for the fact thatremaining_budgetshrinks over time viaallocate(and potentially grows back via a futuredeallocate) between deposit and eventual refund.cancel_milestone's (and any future timeout-triggered) refund ofremaining_budget.allocate/admin-authorization implications:allocate/release_issue/cancel_milestoneremain 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 forextend_deadline.Acceptance Criteria
milestone_id, with correct proportional refund ofremaining_budgetat cancellationcargo test --workspacepassesAdditional Notes
create_milestone's single-sponsor design atcontracts/milestones/src/lib.rs:54-87;cancel_milestone's single-sponsor refund atcontracts/milestones/src/lib.rs:201-209.test_multi_sponsor_milestone_proportional_refund_after_partial_allocation— sponsor A deposits 700 (via a hypothetical multi-contributioncreate_milestone/contributecall), sponsor B deposits 300 (total 1000);allocate400 to one issue,release_issueit;cancel_milestone—remaining_budgetis 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.compute_splitinto 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).