Skip to content

Fix/fee distribution rounding error#82

Open
caxtonacollins wants to merge 4 commits intoTrustless-Work:single-release-develop_v2from
caxtonacollins:fix/fee-distribution-rounding-error
Open

Fix/fee distribution rounding error#82
caxtonacollins wants to merge 4 commits intoTrustless-Work:single-release-develop_v2from
caxtonacollins:fix/fee-distribution-rounding-error

Conversation

@caxtonacollins
Copy link

CLR-S (2)

Pull Request | Trustless Work

1. Issue Link


2. Brief Description of the Issue

A critical accounting vulnerability was identified in the withdraw_remaining_funds and resolve_dispute functions. The contract transferred total fees upfront and then calculated each recipient's share using floor division. Due to rounding, the sum of individual fee shares was often less than the total fees transferred, leading to the contract attempting to distribute more funds than were actually available in its balance. This caused transaction reverts and potentially locked funds.

Solution

Implemented Strategy 1: Deduct Individual Fees First.
Instead of transferring the total estimated fees upfront, the contract now:

  1. Calculates the individual fee shares (trustless work and platform) for each recipient.
  2. Accumulates these floor-rounded individual shares into actual_trustless_fees and actual_platform_fees.
  3. Calculates the net_amount for each recipient by subtracting their individual fee shares.
  4. Transfers the actually accumulated fees and then the net amounts to recipients.

This ensures that actual_fees + sum(net_amounts) exactly equals the total amount being distributed, maintaining perfect accounting balance.

Changes

  • [MODIFY] contracts/escrow/src/core/dispute.rs: Refactored withdraw_remaining_funds and resolve_dispute to use the new fee accumulation logic.
  • [NEW] contracts/escrow/src/tests/dispute_tests.rs: Added test_dispute_resolution_rounding_edge_case to verify the fix with small amounts (2i128) that previously triggered reverts.

Verification

  • Ran all existing tests (cargo test): Passed (19 tests).
  • Ran new edge case test: Passed.
  • Total tests passed: 20.

Branch

fix/fee-distribution-rounding-error

@coderabbitai
Copy link

coderabbitai bot commented Feb 22, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs).
Share your feedback on Discord.


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 and usage tips.

@caxtonacollins
Copy link
Author

@armandocodecr please review

@caxtonacollins
Copy link
Author

@techrebelgit please review

@caxtonacollins
Copy link
Author

@techrebelgit hellooo

@armandocodecr armandocodecr self-requested a review February 25, 2026 15:56
Copy link
Contributor

@armandocodecr armandocodecr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fix in dispute.rs is correct in both functions (withdraw_remaining_funds and resolve_dispute). The approach of accumulating the actual fees per recipient before transferring (“accumulate-then-transfer”) solves the root bug: now sum(actual_fees) + sum(net_amounts) = total, so the contract never attempts to move more funds than it has. Good work there.

What needs to be corrected

  1. The test does not reproduce the bug.
    With total = 2 and fees of 30 bps + 300 bps, both fees round to 0 (floor(2 * 30 / 10000) = 0). That means the test passes trivially without exercising the rounding vulnerability — the old code would have passed it too. You need to use values where the fees are > 0 and the rounding difference is observable. For example: total = 100_003 with distributions {A: 50_001, B: 50_002} or something similar where sum(floor(amount_i * fee / total)) is strictly less than the calculated total fee.

  2. Missing test for withdraw_remaining_funds.
    The test only covers resolve_dispute, but the same bug existed in withdraw_remaining_funds. Add an equivalent test for that function as well.

  3. Minor cleanup of the test.
    The comment “// Actually, let's use slightly larger small amounts to see some rounding” looks like a draft thought that was left there. Clean it up before merging.

  4. Suggestion (non-blocking): The logic for accumulating fees + net distributions is identical in both functions (~30 lines). It would be good to extract it to a helper function to avoid code duplication, but that can be left for a later refactor.

In summary: the fix is fine, the test needs to be redone with values that actually trigger the rounding error, and coverage needs to be added for withdraw_remaining_funds.

@armandocodecr
Copy link
Contributor

Hi @caxtonacollins! I've left you a couple of comments.

@caxtonacollins
Copy link
Author

Hi @caxtonacollins! I've left you a couple of comments.

@armandocodecr working on it

@caxtonacollins
Copy link
Author

@armandocodecr i have made the changes

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.

2 participants