Skip to content

fix: round refund amounts correctly instead of flooring - #827

Merged
Idrhas merged 1 commit into
Fundable-Protocol:mainfrom
daameeloolaa-png:fix/refund-rounding
Sep 3, 2026
Merged

fix: round refund amounts correctly instead of flooring#827
Idrhas merged 1 commit into
Fundable-Protocol:mainfrom
daameeloolaa-png:fix/refund-rounding

Conversation

@daameeloolaa-png

Copy link
Copy Markdown
Contributor

fix: round refund amounts correctly instead of flooring

Problem

Three separate integer division bugs caused small token amounts to be silently discarded
rather than returned to users during refunds and cancellations.

Changes

contracts/nft-stream/src/lib.rs — calculate_vested

The vested amount was computed as (total_amount * elapsed) / duration, a single
multiply-then-divide. Integer floor division means vested is always rounded down, so
refund_amount = total_amount - vested is inflated — the sender recovers more than owed while
the recipient loses up to duration - 1 base units on cancellation. The intermediate product
also risks overflow for large amounts.

Replaced with the split-multiplication identity already used in payment-stream:

(total / duration) * elapsed + ((total % duration) * elapsed) / duration

contracts/campaign-funding/src/lib.rs — calculate_reserve

The 10% tree-replacement reserve used floor division on the remainder term, so it could come
out 1 stroop short. That dust ended up in the distributable amount instead of the reserve,
silently under-funding it.

Applied ceiling division (+ 9_999 before dividing) to match the existing pattern in
calculate_fee.

contracts/campaign-funding/src/lib.rs — distribute_proceeds

Each team member's share was computed with floor division on the remainder. With multiple
members the per-member dust accumulates and stays locked in the contract permanently.

Earlier members now use ceiling division; the last member receives amount - distributed (the
exact leftover), guaranteeing sum(all shares) == amount with nothing stranded.

Impact

  • No change to the happy path for whole-number amounts.
  • Maximum correction per operation: 1 base unit (1 stroop for XLM, 1 unit for USDC).
  • All three fixes are backwards-compatible — no storage layout changes.

closes #734

Three rounding-down bugs that caused small amounts to be silently
discarded on refunds/cancellations:

1. nft-stream calculate_vested: replaced single multiply-then-divide
   with split-multiplication to avoid both intermediate overflow and
   floor-division error that inflated refund_amount by up to
   (duration - 1) base units.

2. campaign-funding calculate_reserve: applied ceiling division
   (same pattern as calculate_fee) so the 10% reserve is never 1
   stroop short, matching the fee collector's existing behaviour.

3. campaign-funding distribute_proceeds: earlier team members now use
   ceiling division on the remainder term; the last member receives
   the exact leftover, guaranteeing sum(shares) == amount with no
   dust locked in the contract.
@drips-wave

drips-wave Bot commented Sep 3, 2026

Copy link
Copy Markdown

@daameeloolaa-png 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

@coderabbitai

coderabbitai Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Team

Run ID: 612f5f48-b99f-4eee-becb-0035ce0e6607


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@Idrhas
Idrhas merged commit 109598d into Fundable-Protocol:main Sep 3, 2026
2 of 3 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

Development

Successfully merging this pull request may close these issues.

bug(contract): Campaign refund calculation truncates decimal cents

2 participants