Skip to content

[CIP-87] Expose penalty cap on /trades API - #4878

Merged
fleupold merged 5 commits into
mainfrom
trades-penalty-cap
Sep 11, 2026
Merged

[CIP-87] Expose penalty cap on /trades API#4878
fleupold merged 5 commits into
mainfrom
trades-penalty-cap

Conversation

@fleupold

@fleupold fleupold commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Description

Quasilabs complained that the penalty cap is not readable from the API. Since - like protocol fees - they can change from auction to auction, this PR adds them on the same endpoint where executed fee policies can be found, namely the /trades endpoint.

Changes

  • DB function to fetch penalties for a given order, auction id
  • Inject penalty information like protocol fees on the trades struct
  • Update openapi

How to test

Made API check part of the e2e test

@fleupold
fleupold requested a review from a team as a code owner September 8, 2026 14:56
@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown

Reminder: Please consider backward compatibility when modifying the API specification.
If breaking changes are unavoidable, ensure:

  • You explicitly pointed out breaking changes.
  • You communicate the changes to affected teams (at least Frontend team and SAFE team).
  • You provide proper versioning and migration mechanisms.

Caused by:

@claude

claude Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Claude finished @fleupold's task in 2m 57s —— View job


Review: [CIP-87] Expose penalty cap on /trades API

  • Read changed files and gather context
  • Analyze DB query changes
  • Analyze model/API changes
  • Post summary

This looks solid and ready to merge. The implementation mirrors the existing executed_protocol_fees batching pattern closely, so it's consistent with the codebase.

What I checked:

  • ✅ The SQL is correct and defensive: penalty_caps_native[array_position(order_uids, order_uid)] returns NULL both when penalties were disabled (penalty_caps_native IS NULL) and when the position/index isn't found, which filter_map then correctly drops → None. Matches the documented "absent" semantics.
  • ✅ Keying on (auction_id, order_uid) is the right join key and matches how the trade attributes itself to the settling auction.
  • ✅ Batched single query (no N+1), guarded by the existing u16::MAX early-return.
  • HexOrDecimalU256 serializes as a decimal string, consistent with the BigUint OpenAPI ref; the allOf + nullable: true shape matches other nullable fields in the spec.
  • ✅ Model test + e2e coverage (asserting the trade cap is non-zero and matches a persisted DB cap) are good.

Minor, non-blocking observations (no change required):

  • The auction_order_uids.len() >= u16::MAX guard's comment says "sqlx only allows u16::MAX arguments," but both this new query and the existing fee_policies::fetch_all bind 2 parameters per key, so the real bind limit is u16::MAX / 2. This is pre-existing behavior (not introduced here) and only matters at ~32k+ trades for a single owner/order filter, so it's effectively unreachable — just noting it.
  • Small stylistic inconsistency: executed_protocol_fees wraps its own timer inside the method, whereas the penalty_caps timer is inlined at each call site (duplicated across trades and trades_paginated). Not worth changing unless you want symmetry.

Nice touch making the API assertion part of the e2e test.
· branch trades-penalty-cap

@MartinquaXD MartinquaXD left a comment

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.

Logic looks sound but wouldn't it make more sense on the /solver_competition endpoint? Quasilabs even said they would use the native prices from the competition endpoint to convert the native price to some unit they care about.

I'm okay to merge this as is because it's arguably also a property on the specific trade but for the purpose of reasoning about the performance of the solver having this data in the solver competition endpoint seems more practical.

Comment thread crates/orderbook/openapi.yml Outdated
@fleupold

Copy link
Copy Markdown
Contributor Author

Logic looks sound but wouldn't it make more sense on the /solver_competition endpoint?

Yes, I think that would make sense to. The reason I added it here is that, conceptually, I find it closest to the fee policies, which are also only exposed here. If we want to expose more things about the order on the solver_competition endpoint, I think we would need to redesign that schema a bit. If many solution settle the same order, we probably don't want to repeat a ton of information which is the same for the same order.

@fleupold
fleupold enabled auto-merge September 11, 2026 08:47
@fleupold
fleupold added this pull request to the merge queue Sep 11, 2026
Merged via the queue into main with commit 9773a44 Sep 11, 2026
23 checks passed
@fleupold
fleupold deleted the trades-penalty-cap branch September 11, 2026 09:07
@github-actions github-actions Bot locked and limited conversation to collaborators Sep 11, 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.

2 participants