Skip to content

Stop consuming uniform clearing prices in the autopilot#4370

Open
jmg-duarte wants to merge 2 commits into
mainfrom
jmgd/remove-ucp/2
Open

Stop consuming uniform clearing prices in the autopilot#4370
jmg-duarte wants to merge 2 commits into
mainfrom
jmgd/remove-ucp/2

Conversation

@jmg-duarte
Copy link
Copy Markdown
Contributor

@jmg-duarte jmg-duarte commented Apr 30, 2026

Description

The autopilot no longer needs per-solution uniform clearing prices: scoring uses auction-level native prices, and on-chain settlement verification reads UCPs straight from the calldata. This PR removes UCPs from the autopilot's domain model and persistence path while keeping the wire format intact, so the change is safe under a rolling k8s deploy.

Changes

  • Drop prices from the autopilot's domain Solution (field, constructor arg, and prices() getter).
  • Remove SolutionError::InvalidPrice and its invalid_price metric label — price validation is no longer performed when ingesting a /solve response.
  • Persist empty arrays into proposed_solutions.price_tokens / price_values. The NOT NULL columns are retained for now (an empty array still satisfies the constraint); they can be dropped in a follow-up.
  • Make the autopilot tolerant of drivers that omit clearingPrices: add #[serde(default)] to the deserializer and emit a debug! log when a driver still sends a non-empty map, so we can chase down stragglers before fully removing the field.
  • Leave the driver emitting clearingPrices on /solve responses (with a deprecation comment) so autopilots running the previous code can still deserialize during the rolling deploy.
  • Mark clearingPrices as deprecated: true in the driver /solve response schema and in the orderbook solver_competition_v2 schema, with a note explaining that recent autopilots will return it empty.

A follow-up PR will remove the driver-side field, the deprecation log, and (optionally) drop the now-unused proposed_solutions.price_tokens / price_values columns.

How to test

  1. cargo nextest run -p autopilot -p driver — unit tests, including winner_selection tests updated to construct solutions without prices.
  2. Hit solver_competition_v2 for an auction produced by the new autopilot and confirm clearingPrices is {}; for an auction produced before the change it remains populated.

Staging

Ran in staging with these orders:

Logs:

@github-actions
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:

Scoring uses auction-level native prices and settlement verification
reads prices from on-chain calldata, so per-solution UCPs are dead
weight. Drop them from the autopilot domain `Solution`, the
`SolutionError::InvalidPrice` variant, and the `proposed_solutions`
price columns (now written as empty arrays).

The `/solve` wire format is preserved for rolling-deploy safety:
- autopilot tolerates a missing `clearingPrices` (`#[serde(default)]`)
- our driver still emits it from existing settlement data

`clearingPrices` is marked `deprecated: true` in the driver and
orderbook OpenAPI specs. A follow-up PR will remove the field, the
deprecation log, and the empty DB columns.
@jmg-duarte jmg-duarte force-pushed the jmgd/remove-ucp/2 branch from d750674 to ebf806c Compare May 7, 2026 08:42
@jmg-duarte jmg-duarte marked this pull request as ready for review May 7, 2026 10:29
@jmg-duarte jmg-duarte requested a review from a team as a code owner May 7, 2026 10:29
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 deprecates and removes the use of uniform clearing prices across the autopilot and driver components. Key changes include removing the prices field from the Solution domain model, updating the persistence layer to store empty price vectors, and marking clearing_prices as deprecated in the OpenAPI specifications and DTOs to maintain backward compatibility during rolling deployments. No critical issues found; I have no feedback to provide.

Copy link
Copy Markdown
Member

@AryanGodara AryanGodara 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 one comment regarding (possible) dead code removal after this PR merges

impl Solution {
pub fn into_domain(
self,
) -> Result<domain::competition::Solution, domain::competition::SolutionError> {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

A question regarding post-PR cleanup.

now that InvalidPrice is gone, Solution::into_domain always returns Ok(...), which makes the Result<_, SolutionError> wrapper dead end-to-end.

Metrics::solution_err (run_loop.rs:1006), the filter_map over solutions (run_loop.rs:641), and the partition_result in shadow.rs:230 all become unreachable.

The two remaining SolutionError variants (ZeroScore, SolverDenyListed) have no construction sites anywhere in the workspace; that part was already preexisting, but this PR is now makes the wrapper itself unreachable

I tried removing this locally, and it works cleanly. ie, tests/linter/check all passing 👀

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Will take a look, good find!

self,
) -> Vec<Result<domain::competition::Solution, domain::competition::SolutionError>> {
for solution in &self.solutions {
if !solution.clearing_prices.is_empty() {
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.

nit: we only care about whether the driver still sends UCP for any solutions, right?
In that case we could just emit a single log.

Comment on lines +251 to +252
price_tokens: vec![],
price_values: vec![],
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.

Could you explain how we are going to compute captured fees going forward?
I remember a discussion with the solver team stating that instead of reading the "original" token price from the calldata we would read them from the DB but this is now also dropping that information, right?

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.

3 participants