Skip to content

Round protocol fee up in quote computation#4389

Open
anxolin wants to merge 2 commits intomainfrom
round-up-quote-protocol-fee
Open

Round protocol fee up in quote computation#4389
anxolin wants to merge 2 commits intomainfrom
round-up-quote-protocol-fee

Conversation

@anxolin
Copy link
Copy Markdown

@anxolin anxolin commented May 8, 2026

Summary

Make fee rounding consistent.

Takes a pessimistic approach, the one who yields a:

  • buyAmount for sell orders, which allows solvers to keep all fees and still fulfill the order. If we round it down, we might be promising the user one 1wei more, so when the solver takes the fee, the order might not be fillable.
  • sellAmount for buy orders should also round fees up, which makes the user to sign 1wei more so we can fill the order after taking the fees

Context

I noticed a small divergence between what the quote endpoint returns, and what debug.cow.fi thinks it return based on its own calculations.

See this order: 0xd129e1a85ec79443540264ee8bb37841c9c4d5850c9b1c5ea6e1d764e8aff3e7cfe5660d6c55906ec8c488a466bd4f77f77eec8869fdbaeb

image

The tool calculates the amount at slippage=0, which is basically what the API returns. In this case, returns 63774986, but if you check the logs you see 63774987 (it overpromises by 1 wei). See https://victorialogs.dev.cow.fi/goto/aflg4t470vz7kc?orgId=1

Details

The volume-based protocol fee applied during quote computation in crates/orderbook/src/quoter.rs previously used floor division (checked_div), while the network fee in crates/shared/src/fee.rs uses ceiling. This PR aligns both to the same pessimistic policy: always round protocol fee UP.

Effect at the wei level on inexact divisions:

  • Sell orders: adjusted buy_amount = quote.buy_amount − ceil(buy * factor / scale) — user receives slightly less buy, never more than the protocol can cover.
  • Buy orders: adjusted sell_amount = quote.sell_amount + ceil((sell + network_fee) * factor / scale) — user pays slightly more sell, never less than required.

The change touches only quote-time amounts shown to users; settlement-time fee accounting is unchanged.

Test plan

  • cargo test -p orderbook quoter::tests — 9/9 pass, including two new tests (test_volume_fee_rounds_up_sell_order, test_volume_fee_rounds_up_buy_order) that exercise non-exact divisions where floor and ceil diverge (12345 * 100 / 1_000_000 = 1.2345 → ceil = 2).
  • cargo check -p orderbook
  • CI green

Make the volume-based protocol fee applied during quoting use ceiling
division instead of floor, matching the pessimistic rounding already
used for the network fee in `crates/shared/src/fee.rs`. The quote now
never undercharges users at the wei level: SELL orders deliver a
slightly smaller buy_amount, BUY orders quote a slightly larger
sell_amount on inexact divisions.
@anxolin anxolin marked this pull request as ready for review May 8, 2026 13:36
@anxolin anxolin requested a review from a team as a code owner May 8, 2026 13:36
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request updates the protocol fee calculation in get_vol_fee_adjusted_quote_data to use ceiling rounding, ensuring fees are rounded up for both sell and buy orders. It also adds an explicit zero-check for the division scale and includes unit tests to verify the new rounding logic. I have no feedback to provide.

Copy link
Copy Markdown
Contributor

@jmg-duarte jmg-duarte left a comment

Choose a reason for hiding this comment

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

Overall looks good to me and makes sense, just left some notes for small improvements

Comment on lines +576 to +578
// observe the ceiling. factor 0.0001 -> high_precision = 100, scale =
// 1_000_000, so fee = buy_amount * 100 / 1_000_000 = buy_amount / 10_000.
// Use buy_amount = 12345 so 12345 / 10000 = 1.2345 -> ceil = 2.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The way this is formatted is a bit hard to read and understand

Especially the factor 0.0001 -> high_precision = 100, I don't understand what you mean

Comment on lines +235 to +237
if scale.is_zero() {
return Err(anyhow::anyhow!("volume fee calculation division by zero"));
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nice, makes the rest of the code much cleaner

Comment thread crates/orderbook/src/quoter.rs Outdated
Co-authored-by: José Duarte <15343819+jmg-duarte@users.noreply.github.com>
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