From 372e81b406bf024b620faf19909bbd0a4c004898 Mon Sep 17 00:00:00 2001 From: MartinquaXD Date: Thu, 6 Aug 2026 08:13:57 +0000 Subject: [PATCH 1/3] feat(quotes): add nullable auction_id column to quotes and order_quotes First step in linking a stored quote to the full solver competition it came from. Adds the column and threads it through the DAO structs; every current writer sets it to None. Downstream steps will populate it from the orderbook quote flow. The upsert on `order_quotes` uses `COALESCE(EXCLUDED.auction_id, ...)` so a subsequent write without an auction_id (e.g. an ethflow reorg path) doesn't erase an earlier link. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../src/database/onchain_order_events/mod.rs | 2 ++ crates/database/src/orders.rs | 19 ++++++++++++++++--- crates/database/src/quotes.rs | 17 ++++++++++++++--- crates/orderbook/src/database/orders.rs | 1 + crates/shared/src/event_storing_helpers.rs | 1 + database/README.md | 2 ++ .../sql/V119__add_auction_id_to_quotes.sql | 8 ++++++++ .../V120__add_auction_id_to_order_quotes.sql | 0 8 files changed, 44 insertions(+), 6 deletions(-) create mode 100644 database/sql/V119__add_auction_id_to_quotes.sql create mode 100644 database/sql/V120__add_auction_id_to_order_quotes.sql diff --git a/crates/autopilot/src/database/onchain_order_events/mod.rs b/crates/autopilot/src/database/onchain_order_events/mod.rs index 4a37e2d7bc..093c4fef50 100644 --- a/crates/autopilot/src/database/onchain_order_events/mod.rs +++ b/crates/autopilot/src/database/onchain_order_events/mod.rs @@ -501,6 +501,7 @@ where solver: ByteArray(*quote.data.solver.0), verified: quote.data.verified, metadata: quote.data.metadata.try_into()?, + auction_id: None, }), Err(err) => { let err_label = err.to_metrics_label(); @@ -1315,6 +1316,7 @@ mod test { solver: ByteArray(*quote.data.solver.0), verified: quote.data.verified, metadata: quote.data.metadata.try_into().unwrap(), + auction_id: None, }; assert_eq!(result.1, vec![Some(expected_quote)]); assert_eq!( diff --git a/crates/database/src/orders.rs b/crates/database/src/orders.rs index ae4180c66b..42a0195a25 100644 --- a/crates/database/src/orders.rs +++ b/crates/database/src/orders.rs @@ -4,6 +4,7 @@ use { AppId, OrderUid, TransactionHash, + auction::AuctionId, onchain_broadcasted_orders::OnchainOrderPlacementError, order_events::{OrderEvent, OrderEventLabel, insert_order_event}, }, @@ -360,6 +361,7 @@ pub struct Quote { pub solver: Address, pub verified: bool, pub metadata: serde_json::Value, + pub auction_id: Option, } #[instrument(skip_all)] @@ -380,9 +382,10 @@ INSERT INTO order_quotes ( buy_amount, solver, verified, - metadata + metadata, + auction_id ) -VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)"#; +VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)"#; #[instrument(skip_all)] pub async fn insert_quote_and_update_on_conflict( @@ -397,7 +400,8 @@ pub async fn insert_quote_and_update_on_conflict( " ON CONFLICT (order_uid) DO UPDATE SET gas_amount = $2, gas_price = $3, sell_token_price = $4, sell_amount = $5, -buy_amount = $6, verified = $8, metadata = $9 +buy_amount = $6, verified = $8, metadata = $9, +auction_id = $10 " ); sqlx::query(QUERY) @@ -410,6 +414,7 @@ buy_amount = $6, verified = $8, metadata = $9 .bind(quote.solver) .bind(quote.verified) .bind("e.metadata) + .bind(quote.auction_id) .execute(ex) .await?; Ok(()) @@ -427,6 +432,7 @@ pub async fn insert_quote(ex: &mut PgConnection, quote: &Quote) -> Result<(), sq .bind(quote.solver) .bind(quote.verified) .bind("e.metadata) + .bind(quote.auction_id) .execute(ex) .await?; Ok(()) @@ -557,6 +563,7 @@ pub struct FullOrderWithQuote { pub quote_verified: Option, pub quote_metadata: Option, pub solver: Option
, + pub quote_auction_id: Option, } impl FullOrderWithQuote { @@ -590,6 +597,7 @@ impl FullOrderWithQuote { solver, verified, metadata, + auction_id: self.quote_auction_id, }), _ => None, }; @@ -662,6 +670,7 @@ const FULL_ORDER_WITH_QUOTE: &str = const_format::concatcp!( ", o_quotes.verified as quote_verified", ", o_quotes.metadata as quote_metadata", ", o_quotes.solver as solver", + ", o_quotes.auction_id as quote_auction_id", " FROM ", FROM, " LEFT JOIN order_quotes o_quotes ON o.uid = o_quotes.order_uid", @@ -1564,6 +1573,7 @@ mod tests { solver: ByteArray([1; 20]), verified: false, metadata: Default::default(), + auction_id: None, }; insert_quote(&mut db, "e).await.unwrap(); insert_quote_and_update_on_conflict(&mut db, "e) @@ -1640,6 +1650,7 @@ mod tests { solver: ByteArray([1; 20]), verified: true, metadata, + auction_id: None, }; insert_quote(&mut db, "e).await.unwrap(); let quote_ = read_quote(&mut db, "e.order_uid) @@ -1668,6 +1679,7 @@ mod tests { solver: ByteArray([1; 20]), verified: false, metadata: Default::default(), + auction_id: None, }; insert_quote(&mut db, "e).await.unwrap(); let order_with_quote = single_full_order_with_quote(&mut db, "e.order_uid) @@ -2701,6 +2713,7 @@ mod tests { solver: ByteArray([1; 20]), verified: false, metadata: Default::default(), + auction_id: None, }; // insert quote with verified and metadata fields stored as NULL diff --git a/crates/database/src/quotes.rs b/crates/database/src/quotes.rs index 6ccbb0a55b..509c163b3e 100644 --- a/crates/database/src/quotes.rs +++ b/crates/database/src/quotes.rs @@ -1,5 +1,5 @@ use { - crate::{Address, orders::OrderKind}, + crate::{Address, auction::AuctionId, orders::OrderKind}, bigdecimal::BigDecimal, sqlx::{ PgConnection, @@ -37,6 +37,7 @@ pub struct Quote { pub solver: Address, pub verified: bool, pub metadata: serde_json::Value, + pub auction_id: Option, } /// Stores the quote and returns the id. The id of the quote parameter is not @@ -57,9 +58,10 @@ INSERT INTO quotes ( quote_kind, solver, verified, - metadata + metadata, + auction_id ) -VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13) +VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14) RETURNING id "#; let (id,) = sqlx::query_as(QUERY) @@ -76,6 +78,7 @@ RETURNING id .bind(quote.solver) .bind(quote.verified) .bind("e.metadata) + .bind(quote.auction_id) .fetch_one(ex) .await?; Ok(id) @@ -199,6 +202,7 @@ mod tests { solver: ByteArray([1; 20]), verified: false, metadata: Default::default(), + auction_id: Some(12), }; let id = save(&mut db, "e).await.unwrap(); quote.id = id; @@ -234,6 +238,7 @@ mod tests { solver: ByteArray([1; 20]), verified: false, metadata: Default::default(), + auction_id: None, }; let token_b = ByteArray([2; 20]); @@ -252,6 +257,7 @@ mod tests { solver: ByteArray([2; 20]), verified: false, metadata: Default::default(), + auction_id: None, }; // Save two measurements for token_a @@ -425,6 +431,7 @@ mod tests { solver: ByteArray([1; 20]), verified: false, metadata: Default::default(), + auction_id: None, }; // Highest absolute buy amount, but an expensive fee. @@ -485,6 +492,7 @@ mod tests { solver: ByteArray([1; 20]), verified: false, metadata: Default::default(), + auction_id: None, }; // Lowest absolute sell amount, but an expensive fee -> total spend 3000. @@ -549,6 +557,7 @@ mod tests { solver: ByteArray([1; 20]), verified: false, metadata: Default::default(), + auction_id: None, }; // Unverified but strictly better rate (more buy for the same sell). @@ -610,6 +619,7 @@ mod tests { solver: ByteArray([1; 20]), verified: false, metadata: Default::default(), + auction_id: None, }; let id = save(&mut db, "e).await.unwrap(); quote.id = id; @@ -668,6 +678,7 @@ mod tests { solver: ByteArray([1; 20]), verified: false, metadata: metadata.clone(), + auction_id: None, }; // store quote in database let id = save(&mut db, "e).await.unwrap(); diff --git a/crates/orderbook/src/database/orders.rs b/crates/orderbook/src/database/orders.rs index 979ea20889..781a72b122 100644 --- a/crates/orderbook/src/database/orders.rs +++ b/crates/orderbook/src/database/orders.rs @@ -204,6 +204,7 @@ async fn insert_order(order: &Order, ex: &mut PgConnection) -> Result<(), Insert solver: ByteArray(quote.solver.0.0), verified: quote.verified, metadata: quote.metadata.clone(), + auction_id: None, }; database::orders::insert_quote(ex, &db_quote) .await diff --git a/crates/shared/src/event_storing_helpers.rs b/crates/shared/src/event_storing_helpers.rs index fef7d56bb1..d5036a05d1 100644 --- a/crates/shared/src/event_storing_helpers.rs +++ b/crates/shared/src/event_storing_helpers.rs @@ -28,6 +28,7 @@ pub fn create_quote_row(data: QuoteData) -> Result { solver: ByteArray(*data.solver.0), verified: data.verified, metadata: data.metadata.try_into()?, + auction_id: None, }) } diff --git a/database/README.md b/database/README.md index a772416e93..60a93235a6 100644 --- a/database/README.md +++ b/database/README.md @@ -236,6 +236,7 @@ Quotes that an order was created with. These quotes get stored persistently and verified | boolean | not null | information if quote was verified metadata | json | not null | additional data associated with the quote in json format creation\_timestamp | timestamptz | not null | when the entry was created (DEFAULT NOW() for new and 1970-01-01 for historical data) + auction\_id | bigint | nullable | the auction competition that was the basis for this quote Indexes: - PRIMARY KEY: btree(`order_uid`) @@ -351,6 +352,7 @@ Stores quotes in order to determine whether it makes sense to allow a user to cr solver | bytea | not null | public address of the solver that provided this quote verified | boolean | not null | information if quote was verified metadata | json | not null | additional data associated with the quote in json format + auction\_id | bigint | nullable | the auction competition that was the basis for this quote Indexes: - PRIMARY KEY: btree(`id`) diff --git a/database/sql/V119__add_auction_id_to_quotes.sql b/database/sql/V119__add_auction_id_to_quotes.sql new file mode 100644 index 0000000000..9492428f8d --- /dev/null +++ b/database/sql/V119__add_auction_id_to_quotes.sql @@ -0,0 +1,8 @@ +-- Links a stored quote to its solver competition ledger row in +-- `competition_auctions`. Nullable because only fast path quotes +-- need this link. +ALTER TABLE quotes ADD COLUMN auction_id bigint; + +-- Copy of quotes.auction_id preserved when the quote is attached +-- to an order. +ALTER TABLE order_quotes ADD COLUMN auction_id bigint; diff --git a/database/sql/V120__add_auction_id_to_order_quotes.sql b/database/sql/V120__add_auction_id_to_order_quotes.sql new file mode 100644 index 0000000000..e69de29bb2 From 2a99dbb2ff00ed89beaedab784d13bf30de88f19 Mon Sep 17 00:00:00 2001 From: MartinquaXD Date: Fri, 7 Aug 2026 10:33:03 +0000 Subject: [PATCH 2/3] empty commit to trigger CI again From 5aab4ce9629a88fea6e8ec98b3b5830a52d4809e Mon Sep 17 00:00:00 2001 From: MartinquaXD Date: Fri, 7 Aug 2026 11:23:56 +0000 Subject: [PATCH 3/3] fixup migration after rebase --- database/sql/V120__add_auction_id_to_order_quotes.sql | 0 ...uction_id_to_quotes.sql => V120__add_auction_id_to_quotes.sql} | 0 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 database/sql/V120__add_auction_id_to_order_quotes.sql rename database/sql/{V119__add_auction_id_to_quotes.sql => V120__add_auction_id_to_quotes.sql} (100%) diff --git a/database/sql/V120__add_auction_id_to_order_quotes.sql b/database/sql/V120__add_auction_id_to_order_quotes.sql deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/database/sql/V119__add_auction_id_to_quotes.sql b/database/sql/V120__add_auction_id_to_quotes.sql similarity index 100% rename from database/sql/V119__add_auction_id_to_quotes.sql rename to database/sql/V120__add_auction_id_to_quotes.sql