Skip to content

apply haircut logic in pre-processing#4417

Merged
extrawurst merged 11 commits into
mainfrom
haircut-make-room
May 22, 2026
Merged

apply haircut logic in pre-processing#4417
extrawurst merged 11 commits into
mainfrom
haircut-make-room

Conversation

@extrawurst
Copy link
Copy Markdown
Contributor

Description

The driver applies a per-solver haircut (haircut_bps) to the solution it receives from a solver before submitting on-chain: the user receives less buy-token (sell orders) or pays more sell-token (buy orders) than the solver reported. The solver doesn't know about this and bids against the user's signed limit price.

When the haircut is larger than the solver's surplus over the limit, the post-haircut delivery falls below the signed limit and settle() reverts with GPv2: limit price not respected.

Evidence (prod, order 0xa978e3ec…6a020c06)

  • Mainnet sell order: 500 USDC → buy ≥ 441_289_983_646_158_011_001 SATO (signed limit B).
  • Solver 0x4c52…f739 bid executed_buy = 444_985_590_464_474_499_347 SATO — ~83 bps over B.
  • Driver applied its configured haircut, encoded the trade with buyAmount = B, contract simulation reverted:
    discarded solution: settlement encoding
    err  = Simulation(Revert): execution reverted: GPv2: limit price not respected
    solver = 0x4c52…f739
    
    3 such discards across ~90 s for this single order; eventually expired.

Fix

Tighten the order limits the driver sends to the solver by the haircut factor, mirroring the volume-fee adjustment already done a few lines above:

  • Sell orders: buy.amount := buy.amount / (1 − h)
  • Buy orders: sell.amount := sell.amount / (1 + h)

Any solver bid E that clears the tightened limit survives the post-hoc haircut by construction:

E ≥ B / (1 − h) ⇒ E · (1 − h) ≥ B

Solvers that can't deliver the tightened limit fail their own satisfies check and don't bid — strictly better than bidding a solution that's guaranteed to revert.

Test plan

New unit tests in infra::solver::dto::auction::tests:

cargo test -p driver --lib -- infra::solver::dto::auction::tests
  • haircut_zero_is_noophaircut_bps = 0 leaves both sell and buy untouched for either side.
  • haircut_tightens_buy_for_sell_order — exact prod numbers from 0xa978e3ec…; asserts buy.amount == B / (1 − h) and that the round-trip post_haircut ≥ B holds within f64 rounding.
  • haircut_tightens_sell_for_buy_order — symmetric for buy orders.
  • haircut_overflow_preserves_original_limit — explicit guard that a failing apply_factor does NOT zero the limit.

Existing end-to-end tests tests/cases/haircut.rs::{order_haircut_reduces_score, buy_order_haircut} continue to pass; the test-side mirror in tests/setup/solver.rs keeps the stub's expected auction JSON aligned with what the driver now sends.

Notes

  • No-op for solvers with haircut_bps = 0 (the default). Only affects solvers explicitly configured with a haircut.

* add test for haircut math
* reuse BPS defintion
@extrawurst extrawurst requested a review from a team as a code owner May 15, 2026 19:00
@extrawurst extrawurst changed the title apply haircut logic in pre-processing to make-room apply haircut logic in pre-processing May 15, 2026
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 introduces a mechanism to tighten order limits sent to solvers by applying a 'haircut' factor, ensuring that post-haircut executions still respect user-signed limit prices. The implementation includes a new apply_haircut function in the auction DTO and updates to the solver configuration and test mocks. A review comment identifies a potential issue in a debug_assert! that uses a strict inequality for the maximum basis points, which could cause panics in debug builds if the haircut is set to 100%.

Comment thread crates/driver/src/infra/solver/dto/auction.rs
Copy link
Copy Markdown
Contributor

@tilacog tilacog left a comment

Choose a reason for hiding this comment

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

LGTM, just left two minor comments

Comment thread crates/driver/src/infra/solver/dto/auction.rs Outdated
Comment thread crates/driver/src/infra/solver/dto/auction.rs Outdated
Copy link
Copy Markdown
Contributor

@fleupold fleupold left a comment

Choose a reason for hiding this comment

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

I was hoping we could actually model this 💇 concept as an additional fee policy to begin with (this removes code and complexity for two practically identical code paths).

This change is probably the correct fix (and thus might be less risk to ship), but I think it would be a good follow up that can be rolled out with low risk (ie. move one solver from haircut to driver-injected protocol fee, check it works as expected, then remove haircut concept from the codebase).

In the meantime, I'd like to see an integration test for this before merging.

Comment thread crates/driver/src/infra/solver/dto/auction.rs Outdated
Comment thread crates/driver/src/tests/setup/solver.rs
Comment thread crates/driver/src/infra/solver/dto/auction.rs
Comment thread crates/driver/src/infra/solver/dto/auction.rs
Copy link
Copy Markdown
Contributor

@MartinquaXD MartinquaXD left a comment

Choose a reason for hiding this comment

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

Agree that we should rather model the haircut logic as a custom volume fee that gets injected by the driver.

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Comment thread crates/driver/src/infra/solver/dto/auction.rs
@extrawurst
Copy link
Copy Markdown
Contributor Author

Added an e2e test now. As discussed I will merge this now and followup with an attempt to merge this with volume fee separately.

@extrawurst extrawurst enabled auto-merge May 22, 2026 17:46
@extrawurst extrawurst added this pull request to the merge queue May 22, 2026
Merged via the queue into main with commit 21da8f9 May 22, 2026
20 checks passed
@extrawurst extrawurst deleted the haircut-make-room branch May 22, 2026 18:09
@github-actions github-actions Bot locked and limited conversation to collaborators May 22, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants