Skip to content

fix: transition milestones to Paid, paginate funder reports, surface … - #842

Merged
Samuel1505 merged 2 commits into
PhasoraLabs:mainfrom
Favour4712:fix/issue-696-697-698-699-milestone-earnings-pagination-index-notifications
Aug 2, 2026
Merged

fix: transition milestones to Paid, paginate funder reports, surface …#842
Samuel1505 merged 2 commits into
PhasoraLabs:mainfrom
Favour4712:fix/issue-696-697-698-699-milestone-earnings-pagination-index-notifications

Conversation

@Favour4712

Copy link
Copy Markdown
Contributor

Summary

Fixes four correctness/data-integrity bugs in the stellar-grants contract:

Changes

#696contracts/contracts/stellar-grants/src/governance.rs, lib.rs, data_export.rs

  • Added governance::mark_milestones_paid(env, grant_id, total_milestones): transitions every still-Approved milestone to Paid and emits the (previously-defined-but-never-called) MilestonePaid event.
  • Called it from finalize_grant_release right before complete_grant (immediate-release path) and from execute_escrow_release right after a multisig-gated release actually executes — not from inside the payout loop itself, so a milestone is never marked Paid until its fund transfer has actually confirmed (a multisig-pending release correctly leaves it at Approved until execution).
  • compute_total_paid_if_quorum_ready now accepts milestones already in Paid state (not just Approved), since it's re-run a second time from execute_escrow_release after some milestones may already have been marked paid.
  • data_export's approved_at field now also considers Paid milestones (previously it reported None for approved_at the moment a milestone moved past Approved).

#697contracts/contracts/stellar-grants/src/funder_report.rs

  • Added a private all_grant_summaries helper that fetches every grant for a funder (no hardcoded cap), used by get_report, token_summary, and dashboard_summary. total_in_escrow is fixed transitively since it delegates to token_summary.

#698contracts/contracts/stellar-grants/src/grant_index.rs, events.rs

  • push_to_index now takes an explicit cap and returns whether the id ended up in the list; on a cap hit it emits a new IndexCapReached event instead of silently no-opping.
  • Chose the "surfaced cap" fix over full bucketing/restructuring (the issue's stated alternative) to keep the change minimal and low-risk; a durable pagination-free structure remains a larger follow-up if the project wants it.

#699contracts/contracts/stellar-grants/src/lib.rs

  • notification::emit_notification is now called after grant_create (NewGrant, scoped PerContributor(owner)), after milestone submission (MilestoneSubmitted, scoped PerGrant), inside the milestone_vote approved/rejected branches (MilestoneApproved/MilestoneRejected, scoped PerGrant), and after dispute_raise (DisputeRaised, scoped PerGrant).

Build-blocker (pre-existing, unrelated to the four issues above)

  • contracts/contracts/stellar-grants/src/errors.rs: added the missing ContractError::TooManyPublicReviews variant. open_review.rs already referenced it, but it was never added to the enum — this fails cargo clippy --workspace --lib --target wasm32v1-none -- -D warnings (the exact command this repo's CI runs) on main today, independent of this PR. Left everything else about that module untouched.

Tests

  • tests/test_milestone_paid_earnings.rs — full grant_creategrant_fundmilestone_submitmilestone_votegrant_complete flow; asserts the milestone ends up Paid, portfolio_earnings_by_token reflects the payout (and is zero beforehand), export_grants's paid_out reflects it, and the milestone_paid event fires.
  • tests/test_notification_emission.rs — three tests covering NewGrant, MilestoneSubmitted/MilestoneApproved, and DisputeRaised: subscribe, trigger the action, assert a notification event was published (and get_subscribers returns the subscriber).
  • funder_report.rs unit test seeds 55 grants for one funder and asserts get_report, dashboard_summary, and token_summary all reflect the full 55, not just 50.
  • grant_index.rs unit tests use a reduced cap (3) to demonstrate push_to_index now reports failure and emits IndexCapReached instead of silently dropping an entry, and that re-pushing an existing id at a full cap is still a safe no-op.

CI

This repo's CI (.github/workflows/ci.yml, contracts job) runs exactly:

cargo fmt --all -- --check
cargo clippy --workspace --lib --target wasm32v1-none -- -D warnings
cargo check --workspace --target wasm32v1-none

It does not run cargo test. All three commands were run locally against this branch and pass cleanly.

cargo test was also run locally for the new integration tests (tests/test_milestone_paid_earnings.rs, tests/test_notification_emission.rs) and they pass. The crate's internal #[cfg(test)] unit-test target (cargo test --lib) currently fails to compile on main due to extensive pre-existing, unrelated breakage in files this PR doesn't touch (milestone_extension.rs, referral.rs, merkle.rs, split_payment.rs, storage/helpers.rs, compliance.rs, lockup.rs — missing trait imports, a missing Default derive, a moved-value bug, wrong argument counts). Since CI never invokes cargo test, none of this is gated — but it's worth a maintainer's attention separately, and I've intentionally left it out of this PR's scope. The two new unit tests added to funder_report.rs and grant_index.rs are verified correct by hand-tracing (and were confirmed to pass in isolation via a temporary local patch of the unrelated breakage, which was not included in this PR).

One more related finding surfaced while writing the #696/#699 tests: milestone_vote(approve=true) currently requires a milestone to already have a fully-satisfied checklist (checklist::all_required_approved) or it panics with RequiredCriteriaNotMetall_required_approved defaults to false when no checklist was ever attached, and checklists are optional at grant creation. In practice this means no milestone can be approved through the normal reviewer-vote flow unless a checklist was explicitly defined, submitted, and reviewed first, even though nothing in grant_create/milestone_submit requires one. This reproduces on main too (the existing test_event_emission_on_milestone_vote test in tests/test_event_emission.rs already fails this way, unrelated to my changes). I routed around it in my new tests by attaching and clearing a trivial optional checklist criterion, but did not fix the underlying gate — it felt out of scope for these four issues and worth a maintainer decision on intended behavior. Flagging it here rather than silently working around it.

Closes

Closes #696
Closes #697
Closes #698
Closes #699

…index cap, wire notifications (PhasoraLabs#696 PhasoraLabs#697 PhasoraLabs#698 PhasoraLabs#699)

- PhasoraLabs#696: transition milestones to Paid once their real payout confirms
  (finalize_grant_release / execute_escrow_release), instead of leaving
  them stuck at Approved forever. portfolio::earnings_by_token and
  data_export's paid-out totals only ever counted Paid milestones, so
  every normally-paid contributor showed zero earnings.
- PhasoraLabs#697: funder_report's get_report/token_summary/dashboard_summary no
  longer hard-code a 50-grant window; they now aggregate every grant a
  funder has contributed to.
- PhasoraLabs#698: grant_index::push_to_index emits an IndexCapReached event
  instead of silently dropping entries once an index hits
  MAX_INDEX_ENTRIES, so the condition is observable instead of silent
  data loss.
- PhasoraLabs#699: wire notification::emit_notification into grant_create,
  milestone_submit, milestone_vote (approve/reject), and dispute_raise
  so subscribers actually receive NewGrant/MilestoneSubmitted/
  MilestoneApproved/MilestoneRejected/DisputeRaised notifications.

Also adds the ContractError::TooManyPublicReviews variant referenced by
open_review.rs — without it the crate fails to compile at all, which
was breaking `cargo clippy --workspace --lib --target wasm32v1-none`
(the exact command CI runs) even before this branch's changes.
@drips-wave

drips-wave Bot commented Jul 30, 2026

Copy link
Copy Markdown

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

…-699-milestone-earnings-pagination-index-notifications

# Conflicts:
#	contracts/contracts/stellar-grants/src/errors.rs
#	contracts/contracts/stellar-grants/src/lib.rs
@Samuel1505
Samuel1505 merged commit a63298e into PhasoraLabs:main Aug 2, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants