Skip to content

Settle fast-path orders from the autopilot - #4729

Open
AryanGodara wants to merge 3 commits into
actually-store-quote-competition-datafrom
aryan/be-62-autopilot-settle-fastpath
Open

Settle fast-path orders from the autopilot#4729
AryanGodara wants to merge 3 commits into
actually-store-quote-competition-datafrom
aryan/be-62-autopilot-settle-fastpath

Conversation

@AryanGodara

@AryanGodara AryanGodara commented Aug 11, 2026

Copy link
Copy Markdown
Member

Description

When a fast-path order arrives, the autopilot recovers its quote's winning solution and settles it out of competition via the driver's /settle.
Solves BE-62.

Stacked on #4737.

Changes

  • on every new order, look up its quote's auction_id, then recover the winning solution + fill from proposed_solutions / proposed_trade_executions
  • send the order, its fill (limit) prices, and native prices to the driver that produced the quote, matched by solver address
  • add the fastPath payload to the autopilot's /settle DTO

How to test

No automated tests yet: the competition data this reads isn't persisted until BE-56, so the listener is no-op on real orders for now. I verified locally by seeding a competition in local DB (with all migrations) and asserting fast_path_order returns it. The end-to-end test should land once BE-56 and orderbook fast-path support are in.
For those reasons keeping this PR in draft for now

@AryanGodara AryanGodara self-assigned this Aug 11, 2026
@linear-code

linear-code Bot commented Aug 11, 2026

Copy link
Copy Markdown

BE-62

Comment thread crates/autopilot/src/infra/order_notify/fast_path.rs
Comment thread crates/autopilot/src/infra/order_notify/fast_path.rs Outdated
Comment thread crates/autopilot/src/infra/order_notify/fast_path.rs
Comment thread crates/autopilot/src/infra/persistence/mod.rs Outdated
Comment thread crates/autopilot/src/infra/persistence/mod.rs Outdated
Comment thread crates/autopilot/src/infra/solvers/dto/settle.rs
Base automatically changed from aryan/be-58-settle-fastpath-quote to main August 13, 2026 07:09
@AryanGodara
AryanGodara force-pushed the aryan/be-62-autopilot-settle-fastpath branch from b6c9e30 to 9ceb810 Compare August 13, 2026 07:22
@AryanGodara
AryanGodara changed the base branch from main to actually-store-quote-competition-data August 14, 2026 14:51
@AryanGodara
AryanGodara force-pushed the aryan/be-62-autopilot-settle-fastpath branch from 9ceb810 to a2eb919 Compare August 14, 2026 14:54
@AryanGodara
AryanGodara force-pushed the aryan/be-62-autopilot-settle-fastpath branch from d318fbe to 26528a1 Compare August 14, 2026 16:38
@AryanGodara
AryanGodara marked this pull request as ready for review August 14, 2026 16:56
@AryanGodara
AryanGodara requested a review from a team as a code owner August 14, 2026 16:56
@claude

claude Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Claude finished @AryanGodara's task in 4m 29s —— View job


PR Review: Settle fast-path orders from the autopilot

Reviewed against origin/actually-store-quote-competition-data. The earlier review points look addressed — the settle now runs in a spawned task (fast_path.rs:127), initiation is logged with solver/uid/auction_id (fast_path.rs:68), the native-price lookup uses Duration::ZERO, the DB read is a single joined query (single_fast_path_order), and the fill amount is now passed through to the driver (executed/limit_prices). Nice, focused stack.

One substantive finding, posted inline:

  • native_prices isn't actually cache-only on a miss (fast_path.rs:79) — estimate_native_price(_, Duration::ZERO) short-circuits only on a cache hit; a miss falls through to a live, un-time-bounded fetch (fetch_prices guards timeout.is_zero(), the singular method does not). Can blow the fast-path window and the doc comment overstates the behavior.

Nothing else blocking from my side. The executed == limit_prices.sell/buy fill relies on the recorded amount exactly matching the signed order for non-partial fills (Static-fee market orders) — worth keeping an eye on once real competition data flows, but consistent with compare_orders already gating the match.

Comment on lines +79 to +98
/// Native prices for the order's tokens, from the estimator's cache only.
/// Missing prices are omitted; the driver tolerates a partial map.
async fn native_prices(&self, tokens: &[Address]) -> HashMap<Address, U256> {
let mut prices = HashMap::new();
for &token in tokens {
match self
.native_price_estimator
.estimate_native_price(token, Duration::ZERO)
.await
{
Ok(price) => {
if let Some(price) = to_normalized_price(price) {
prices.insert(token, price);
}
}
Err(err) => tracing::warn!(?token, ?err, "no native price for fast-path token"),
}
}
prices
}

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 doc comment says "from the estimator's cache only", but estimate_native_price(token, Duration::ZERO) does not behave as cache-only on a miss.

Looking at CachingNativePriceEstimator::estimate_native_price (crates/price-estimation/src/native_price_cache.rs:417): on a cache hit it returns immediately, but on a miss it falls through to estimate_prices_and_update_cache([token], .., timeout=0), which calls the underlying estimator with a 0 timeout — and, unlike fetch_prices (same file, line 389: if timeout.is_zero() { return prices; }), there is no time::timeout wrapper around it. So a cache miss here triggers a live fetch that can block for the full duration of a real HTTP request.

That's exactly the situation the reviewer wanted to avoid ("the fast path does not leave enough time to fetch them from scratch"). Because this runs in the spawned task it won't stall the notifier, but it can silently blow the fast-path submission window on any uncached token, and with one tokio::spawn per arriving order there's no bound on how many such fetches pile up under load.

fetch_prices(tokens, Duration::ZERO) is the genuinely cache-only path, but it's an inherent method on CachingNativePriceEstimator, not on the NativePriceEstimating trait — so either hold the concrete type here, or add a cache-only accessor to the trait. At minimum, please correct the comment so it doesn't claim cache-only behavior it doesn't have.

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