Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 41 additions & 7 deletions crates/autopilot/src/database/onchain_order_events/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,7 @@ impl<T: Send + Sync + Clone, W: Send + Sync> OnchainOrderParser<T, W> {
order_placement_events: Vec<(ContractEvent, Log)>,
) -> Result<(
Vec<W>,
Vec<i64>,

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.

This return type looks pretty complicated. Can we use a QuoteId alias instead of i64 here or refactor it to return a named struct?

Vec<Option<database::orders::Quote>>,
Vec<(database::events::EventIndex, OnchainOrderPlacement)>,
Vec<Order>,
Expand Down Expand Up @@ -265,7 +266,7 @@ impl<T: Send + Sync + Clone, W: Send + Sync> OnchainOrderParser<T, W> {
.await;

let data_tuple = onchain_order_data.into_iter().map(
|(event_index, quote, onchain_order_placement, order, tx_hash)| {
|(event_index, quote_id, quote, onchain_order_placement, order, tx_hash)| {

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.

with this amount of parameters, I think it's time we make this a function

(
self.custom_onchain_data_parser
.customized_event_data_for_event_index(
Expand All @@ -274,6 +275,7 @@ impl<T: Send + Sync + Clone, W: Send + Sync> OnchainOrderParser<T, W> {
&custom_data_hashmap,
&onchain_order_placement,
),
quote_id,
quote,
(event_index, onchain_order_placement),
order,
Expand Down Expand Up @@ -318,9 +320,9 @@ impl<T: Send + Sync + Clone, W: Send + Sync> OnchainOrderParser<T, W> {
.collect();
let invalidation_events = get_invalidation_events(events)?;
let invalided_order_uids = extract_invalidated_order_uids(invalidation_events)?;
let (custom_onchain_data, quotes, broadcasted_order_data, mut orders, tx_hashes) = self
.extract_custom_and_general_order_data(order_placement_events)
.await?;
let (custom_onchain_data, quote_ids, quotes, broadcasted_order_data, mut orders, tx_hashes) =
self.extract_custom_and_general_order_data(order_placement_events)
.await?;

database::onchain_invalidations::insert_onchain_invalidations(
transaction,
Expand Down Expand Up @@ -364,6 +366,29 @@ impl<T: Send + Sync + Clone, W: Send + Sync> OnchainOrderParser<T, W> {
.await
.context("insert_orders failed")?;

// Promote fast-path quotes for onchain orders (mirrors the trait-based

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.

Aren't we missing applying the validFrom here as well? Probably a sign that the min_fast_path_exclusivity should live only in the autopilot and apply to orders that don't explicitly set a validFrom. Maybe we can "gate" the feature differently in the API?

// path in the orderbook). For each successfully-quoted order tied to a
// fast-path `auction_id`, drop the transient `quotes` row and rewrite
// the placeholder `proposed_trade_executions.order_uid` to the real
// one.
for (quote_id, quote, order) in izip!(&quote_ids, &quotes, &orders) {
let Some(quote) = quote else {
continue;
};
let Some(auction_id) = quote.auction_id else {
continue;
};
// The order_quotes row already carries the auction_id (populated
// inline above by `insert_quotes`), so all that's left is to drop
// the transient `quotes` row and patch competition tables.
database::quotes::delete_and_return_row(transaction, *quote_id)
.await
.context("failed to delete promoted onchain quote")?;
database::fast_path::finalize_quote_competition(transaction, auction_id, order.uid)
.await
.context("failed to patch competition rows for onchain order")?;
}
Comment on lines +374 to +390

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.

This loop patches competition_auctions.order_uids for every order that has a Some quote with an auction_id, regardless of whether the onchain placement actually succeeded. convert_onchain_order_placement returns quote: Ok(..) even for orders it flags as outside_market_price (it only increments a metric), so a failed/invalid fast-path placement would still overwrite the competition row with ARRAY[order_uid]. If ethflow + fast-path can co-occur, consider gating this on placement success (as the insert_quotes comment above at L346-348 implies quotes are only meaningful for orders that make it into an auction).


for order in &invalided_order_uids {
tracing::debug!(?order, "invalidated order");
}
Expand Down Expand Up @@ -442,6 +467,7 @@ fn extract_invalidated_order_uids(

type GeneralOnchainOrderPlacementData = (
EventIndex,
i64,
Option<database::orders::Quote>,
OnchainOrderPlacement,
Order,
Expand Down Expand Up @@ -513,7 +539,14 @@ where
None
}
};
Ok((event_index, quote, order_data.0, order_data.1, tx_hash))
Ok((
event_index,
quote_id,
quote,
order_data.0,
order_data.1,
tx_hash,
))
},
);
let onchain_order_placement_data: Vec<Result<GeneralOnchainOrderPlacementData>> =
Expand Down Expand Up @@ -1319,9 +1352,10 @@ mod test {
metadata: quote.data.metadata.clone().try_into().unwrap(),
auction_id: quote.data.auction_id,
};
assert_eq!(result.1, vec![Some(expected_quote)]);
assert_eq!(result.1, vec![0i64]);
assert_eq!(result.2, vec![Some(expected_quote)]);
assert_eq!(
result.2,
result.3,
vec![(
expected_event_index,
OnchainOrderPlacement {
Expand Down
41 changes: 41 additions & 0 deletions crates/database/src/fast_path.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//! Database queries for the fast-path settlement feature.
//!
//! Fast-path orders reuse a quote's synthetic solver competition as the
//! actual settlement. This module owns the promotion step that patches
//! the placeholder rows written at quote time to reference the real
//! `order_uid` ([`finalize_quote_competition`]).

use {
crate::{OrderUid, PgTransaction, auction::AuctionId},
std::ops::DerefMut,
tracing::instrument,
};

/// Because the final order uid is not known when we store the quote
/// competition data we use `0x000...000` as a sentinel value.
/// When an order gets placed referencing a quote competition this function
/// replaces the placeholder value with the now final order uid.
#[instrument(skip_all)]
pub async fn finalize_quote_competition(
ex: &mut PgTransaction<'_>,
auction_id: AuctionId,
order_uid: OrderUid,
) -> Result<(), sqlx::Error> {
const QUERY: &str = r#"
WITH patch_te AS (
UPDATE proposed_trade_executions
SET order_uid = $1
WHERE auction_id = $2 AND order_uid = $3
)
UPDATE competition_auctions
SET order_uids = ARRAY[$1]
WHERE id = $2
"#;
sqlx::query(QUERY)
.bind(order_uid)
.bind(auction_id)
.bind(OrderUid::default())
.execute(ex.deref_mut())
.await?;
Ok(())
}
1 change: 1 addition & 0 deletions crates/database/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pub mod auction;
pub mod byte_array;
pub mod ethflow_orders;
pub mod events;
pub mod fast_path;
pub mod fee_policies;
pub mod jit_orders;
pub mod last_indexed_blocks;
Expand Down
Loading
Loading