diff --git a/crates/autopilot/src/database/competition.rs b/crates/autopilot/src/database/competition.rs index 8ede03bf10..fcb69b2278 100644 --- a/crates/autopilot/src/database/competition.rs +++ b/crates/autopilot/src/database/competition.rs @@ -17,19 +17,19 @@ use { #[derive(Clone, Debug)] pub enum ExecutedFee { - /// Fee is calculated by the solver and known upfront (before the settlement + /// Fee is determined by user and known upfront (before the settlement /// is finalized). - Solver(U256), + UserFee(U256), /// Fee is unknown before the settlement is finalized and is calculated in /// the postprocessing. Currently only used for limit orders. - Surplus, + SolverDetermined, } impl ExecutedFee { pub fn fee(&self) -> Option<&U256> { match self { - ExecutedFee::Solver(fee) => Some(fee), - ExecutedFee::Surplus => None, + ExecutedFee::UserFee(fee) => Some(fee), + ExecutedFee::SolverDetermined => None, } } } @@ -82,13 +82,15 @@ impl super::Postgres { .context("solver_competition::save")?; for order_execution in &competition.order_executions { - let solver_fee = order_execution.executed_fee.fee().map(u256_to_big_decimal); database::order_execution::save( &mut ex, &ByteArray(order_execution.order_id.0), competition.auction_id, - None, - solver_fee.as_ref(), + order_execution + .executed_fee + .fee() + .map(u256_to_big_decimal) + .as_ref(), ) .await .context("order_execution::save")?; diff --git a/crates/autopilot/src/database/on_settlement_event_updater.rs b/crates/autopilot/src/database/on_settlement_event_updater.rs index da6fd4dae8..0b65462f99 100644 --- a/crates/autopilot/src/database/on_settlement_event_updater.rs +++ b/crates/autopilot/src/database/on_settlement_event_updater.rs @@ -113,15 +113,12 @@ impl super::Postgres { if insert_succesful || matches!(auction_data.auction_id, AuctionId::Centralized(_)) { // update order executions for orders with solver computed fees (limit orders) - // for limit orders, fee is called surplus_fee and is determined by the solver - // therefore, when transaction is settled onchain we calculate the fee and save - // it to DB - for order_execution in auction_data.order_executions { - database::order_execution::update_surplus_fee( + for (order_uid, executed_fee) in auction_data.order_executions { + database::order_execution::update_executed_fee( ex, - &ByteArray(order_execution.0 .0), // order uid + &ByteArray(order_uid.0), auction_data.auction_id.assume_verified(), - &u256_to_big_decimal(&order_execution.1), // order fee + &u256_to_big_decimal(&executed_fee), ) .await .context("insert_missing_order_executions")?; diff --git a/crates/autopilot/src/decoded_settlement.rs b/crates/autopilot/src/decoded_settlement.rs index c25d94c102..92643c1b27 100644 --- a/crates/autopilot/src/decoded_settlement.rs +++ b/crates/autopilot/src/decoded_settlement.rs @@ -5,7 +5,6 @@ use { anyhow::{Context, Result}, bigdecimal::{Signed, Zero}, contracts::GPv2Settlement, - database::orders::OrderClass, ethcontract::{common::FunctionExt, tokens::Tokenize, Address, Bytes, H160, U256}, model::{ order::{OrderKind, OrderUid}, @@ -138,15 +137,13 @@ impl From<(Address, U256, Bytes>)> for DecodedInteraction { #[derive(Debug, Clone)] pub struct OrderExecution { pub order_uid: OrderUid, - pub executed_solver_fee: Option, + pub executed_fee: Option, pub sell_token: H160, pub buy_token: H160, pub sell_amount: U256, pub buy_amount: U256, pub executed_amount: U256, - pub signature: Vec, //encoded signature - // For limit orders the solver computes the fee - pub solver_determines_fee: bool, + pub signature: Vec, // encoded signature } impl TryFrom for OrderExecution { @@ -155,10 +152,7 @@ impl TryFrom for OrderExecution { fn try_from(order: database::orders::OrderExecution) -> std::result::Result { Ok(Self { order_uid: OrderUid(order.order_uid.0), - executed_solver_fee: order - .executed_solver_fee - .as_ref() - .and_then(big_decimal_to_u256), + executed_fee: order.executed_fee.as_ref().and_then(big_decimal_to_u256), sell_token: H160(order.sell_token.0), buy_token: H160(order.buy_token.0), sell_amount: big_decimal_to_u256(&order.sell_amount).context("sell_amount")?, @@ -171,7 +165,6 @@ impl TryFrom for OrderExecution { .encode_for_settlement(H160(order.owner.0)) .to_vec() }, - solver_determines_fee: order.class == OrderClass::Limit, }) } } @@ -255,7 +248,7 @@ impl DecodedSettlement { }) } - /// Returns the total `executed_solver_fee` of this solution converted to + /// Returns the total `executed_fee` of this solution converted to /// the native token. This is only the value used for objective value /// computatations and can theoretically be different from the value of /// fees actually collected by the protocol. @@ -312,10 +305,10 @@ impl DecodedSettlement { // use every `OrderExecution` exactly once. let order = orders.swap_remove(i); - // Update fee only for orders with solver computed fees (limit orders) - if !order.solver_determines_fee { + // Update fee for orders for which is not calculated yet. + if order.executed_fee.is_some() { return None; - } + }; let fees = self.fee(external_prices, &order, trade); @@ -333,8 +326,8 @@ impl DecodedSettlement { order: &OrderExecution, trade: &DecodedTrade, ) -> Option { - let solver_fee = match &order.executed_solver_fee { - Some(solver_fee) => *solver_fee, + let executed_fee = match order.executed_fee { + Some(executed_fee) => executed_fee, None => { // get uniform prices let sell_index = self @@ -385,14 +378,14 @@ impl DecodedSettlement { // converts the order's `solver_fee` which is denominated in `sell_token` to the // native token. - tracing::trace!(?solver_fee, "fee before conversion to native token"); + tracing::trace!(?executed_fee, "fee before conversion to native token"); let fee = external_prices - .try_get_native_amount(order.sell_token, u256_to_big_rational(&solver_fee))?; + .try_get_native_amount(order.sell_token, u256_to_big_rational(&executed_fee))?; tracing::trace!(?fee, "fee after conversion to native token"); Some(Fees { order: order.order_uid, - sell: solver_fee, + sell: executed_fee, native: big_rational_to_u256(&fee).ok()?, }) } @@ -730,25 +723,23 @@ mod tests { let orders = vec![ OrderExecution { order_uid: OrderUid::from_str("0xa8b0c9be7320d1314c6412e6557efd062bb9f97f2f4187f8b513f50ff63597cae995e2a9ae5210feb6dd07618af28ec38b2d7ce163f4d8c4").unwrap(), - executed_solver_fee: Some(48263037u128.into()), + executed_fee: Some(48263037u128.into()), buy_amount: 11446254517730382294118u128.into(), sell_amount: 14955083027u128.into(), sell_token: addr!("dac17f958d2ee523a2206206994597c13d831ec7"), buy_token: Default::default(), executed_amount: 14955083027u128.into(), signature: hex::decode("155ff208365bbf30585f5b18fc92d766e46121a1963f903bb6f3f77e5d0eaefb27abc4831ce1f837fcb70e11d4e4d97474c677469240849d69e17f7173aead841b").unwrap(), - solver_determines_fee: false, }, OrderExecution { order_uid: OrderUid::from_str("0x82582487739d1331572710a9283dc244c134d323f309eb0aac6c842ff5227e90f352bffb3e902d78166a79c9878e138a65022e1163f4d8bb").unwrap(), - executed_solver_fee: Some(127253135942751092736u128.into()), + executed_fee: Some(127253135942751092736u128.into()), buy_amount: 1236593080.into(), sell_amount: 5701912712048588025933u128.into(), sell_token: addr!("f4d2888d29d722226fafa5d9b24f9164c092421e"), buy_token: Default::default(), executed_amount: 5701912712048588025933u128.into(), signature: hex::decode("882a1c875ff1316bb79bde0d0792869f784d58097d8489a722519e6417c577cf5cc745a2e353298dea6514036d5eb95563f8f7640e20ef0fd41b10ccbdfc87641b").unwrap(), - solver_determines_fee: false, } ]; let fees = settlement @@ -806,14 +797,13 @@ mod tests { let orders = vec![ OrderExecution { order_uid: OrderUid::from_str("0xaa6ff3f3f755e804eefc023967be5d7f8267674d4bae053eaca01be5801854bf6c7f534c81dfedf90c9e42effb410a44e4f8ef1064690e05").unwrap(), - executed_solver_fee: None, + executed_fee: None, buy_amount: 11446254517730382294118u128.into(), // irrelevant sell_amount: 14955083027u128.into(), // irrelevant sell_token: addr!("ba386a4ca26b85fd057ab1ef86e3dc7bdeb5ce70"), buy_token: addr!("c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"), executed_amount: 134069619089011499167823218927u128.into(), signature: hex::decode("f8ad81db7333b891f88527d100a06f23ff4d7859c66ddd71514291379deb8ff660f4fb2a24173eaac5fad2a124823e968686e39467c7f3054c13c4b70980cc1a1c").unwrap(), - solver_determines_fee: true, }, ]; let fees = settlement @@ -920,14 +910,13 @@ mod tests { let orders = vec![ OrderExecution { order_uid: Default::default(), - executed_solver_fee: Some(463182886014406361088u128.into()), + executed_fee: Some(463182886014406361088u128.into()), buy_amount: 89238894792574185u128.into(), sell_amount: 3026871740084629982950u128.into(), sell_token: addr!("f88baf18fab7e330fa0c4f83949e23f52fececce"), buy_token: addr!("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"), executed_amount: 0.into(), signature: hex::decode("4935ea3f24155f6757df94d8c0bc96665d46da51e1a8e39d935967c9216a60912fa50a5393a323d453c78d179d0199ddd58f6d787781e4584357d3e0205a76001c").unwrap(), - solver_determines_fee: false, }, ]; let fees = settlement diff --git a/crates/autopilot/src/run_loop.rs b/crates/autopilot/src/run_loop.rs index 7a36346b96..7d6a0c9ebb 100644 --- a/crates/autopilot/src/run_loop.rs +++ b/crates/autopilot/src/run_loop.rs @@ -182,10 +182,10 @@ impl RunLoop { match auction_order { Some(auction_order) => { let executed_fee = match auction_order.solver_determines_fee() { - // we don't know the surplus fee in advance. will be populated + // we don't know the executed fee in advance. will be populated // after the transaction containing the order is mined - true => ExecutedFee::Surplus, - false => ExecutedFee::Solver(auction_order.metadata.solver_fee), + true => ExecutedFee::SolverDetermined, + false => ExecutedFee::UserFee(auction_order.metadata.solver_fee), }; order_executions.push(OrderExecution { order_id: *order_id, diff --git a/crates/database/src/order_execution.rs b/crates/database/src/order_execution.rs index fed8f6f7bd..7aaca01178 100644 --- a/crates/database/src/order_execution.rs +++ b/crates/database/src/order_execution.rs @@ -9,39 +9,37 @@ pub async fn save( ex: &mut PgConnection, order: &OrderUid, auction: AuctionId, - surplus_fee: Option<&BigDecimal>, - solver_fee: Option<&BigDecimal>, + executed_fee: Option<&BigDecimal>, ) -> Result<(), sqlx::Error> { const QUERY: &str = r#" -INSERT INTO order_execution (order_uid, auction_id, reward, surplus_fee, solver_fee) +INSERT INTO order_execution (order_uid, auction_id, reward, executed_fee) VALUES ($1, $2, $3, $4, $5) ;"#; sqlx::query(QUERY) .bind(order) .bind(auction) .bind(0.) // reward is deprecated but saved for historical analysis - .bind(surplus_fee) - .bind(solver_fee) + .bind(executed_fee) .execute(ex) .await?; Ok(()) } -// update already existing order_execution record with surplus_fee for partial +// update already existing order_execution record with executed_fee for partial // limit orders -pub async fn update_surplus_fee( +pub async fn update_executed_fee( mut ex: &mut PgConnection, order: &OrderUid, auction: AuctionId, - surplus_fee: &BigDecimal, + executed_fee: &BigDecimal, ) -> Result<(), sqlx::Error> { const QUERY: &str = r#" UPDATE order_execution -SET surplus_fee = $1 +SET executed_fee = $1 WHERE order_uid = $2 AND auction_id = $3 ;"#; sqlx::query(QUERY) - .bind(surplus_fee) + .bind(executed_fee) .bind(order) .bind(auction) .execute(ex.deref_mut()) @@ -60,18 +58,10 @@ mod tests { let mut db = db.begin().await.unwrap(); crate::clear_DANGER_(&mut db).await.unwrap(); - save(&mut db, &Default::default(), 0, None, Default::default()) + save(&mut db, &Default::default(), 0, None).await.unwrap(); + + save(&mut db, &Default::default(), 1, Some(&Default::default())) .await .unwrap(); - - save( - &mut db, - &Default::default(), - 1, - Some(&Default::default()), - Default::default(), - ) - .await - .unwrap(); } } diff --git a/crates/database/src/orders.rs b/crates/database/src/orders.rs index 5d4843caef..16d4c524e6 100644 --- a/crates/database/src/orders.rs +++ b/crates/database/src/orders.rs @@ -455,8 +455,7 @@ pub struct FullOrder { pub ethflow_data: Option<(Option, i64)>, pub onchain_user: Option
, pub onchain_placement_error: Option, - pub executed_surplus_fee: BigDecimal, - pub executed_solver_fee: BigDecimal, + pub executed_fee: BigDecimal, pub full_app_data: Option>, } @@ -519,8 +518,7 @@ array(Select (p.target, p.value, p.data) from interactions p where p.order_uid = where eth_o.uid = o.uid limit 1) as ethflow_data, (SELECT onchain_o.sender from onchain_placed_orders onchain_o where onchain_o.uid = o.uid limit 1) as onchain_user, (SELECT onchain_o.placement_error from onchain_placed_orders onchain_o where onchain_o.uid = o.uid limit 1) as onchain_placement_error, -COALESCE((SELECT SUM(surplus_fee) FROM order_execution oe WHERE oe.order_uid = o.uid), 0) as executed_surplus_fee, -COALESCE((SELECT SUM(solver_fee) FROM order_execution oe WHERE oe.order_uid = o.uid), 0) as executed_solver_fee, +COALESCE((SELECT SUM(executed_fee) FROM order_execution oe WHERE oe.order_uid = o.uid), 0) as executed_fee, (SELECT full_app_data FROM app_data ad WHERE o.app_data = ad.contract_app_data LIMIT 1) as full_app_data "#; @@ -586,8 +584,8 @@ WHERE #[derive(Debug, Clone, Default, PartialEq, sqlx::FromRow)] pub struct OrderExecution { pub order_uid: OrderUid, - /// The `solver_fee` that got executed for this specific fill. - pub executed_solver_fee: Option, + // Uknown for limit orders before the setlement is finalized. + pub executed_fee: Option, pub sell_token: Address, pub buy_token: Address, pub kind: OrderKind, @@ -613,7 +611,7 @@ pub fn order_executions_in_tx<'a>( {SETTLEMENT_LOG_INDICES} SELECT oe.order_uid AS order_uid, - oe.solver_fee AS executed_solver_fee, + oe.executed_fee AS executed_fee, o.sell_token, o.buy_token, o.sell_amount, @@ -1822,11 +1820,11 @@ mod tests { .await .unwrap() .unwrap(); - assert_eq!(order.executed_surplus_fee, 0.into()); + assert_eq!(order.executed_fee, 0.into()); let fee: BigDecimal = 1.into(); let solver_fee: BigDecimal = 2.into(); - crate::order_execution::save(&mut db, &order_uid, 0, Some(&fee), Some(&solver_fee)) + crate::order_execution::save(&mut db, &order_uid, 0, Some(&fee)) .await .unwrap(); @@ -1834,8 +1832,7 @@ mod tests { .await .unwrap() .unwrap(); - assert_eq!(order.executed_surplus_fee, fee); - assert_eq!(order.executed_solver_fee, solver_fee); + assert_eq!(order.executed_fee, solver_fee); } #[tokio::test] @@ -1873,15 +1870,9 @@ mod tests { .unwrap(); let auction_id = 6124819; - crate::order_execution::save( - &mut db, - &order_uid, - auction_id, - None, - Some(&bigdecimal(463182886014406361088)), - ) - .await - .unwrap(); + crate::order_execution::save(&mut db, &order_uid, auction_id, None) + .await + .unwrap(); crate::events::insert_trade( &mut db, @@ -1924,7 +1915,7 @@ mod tests { executions, vec![OrderExecution { order_uid, - executed_solver_fee: Some(bigdecimal(463182886014406361088)), + executed_fee: Some(bigdecimal(463182886014406361088)), sell_token: ByteArray(hex!("f88baf18fab7e330fa0c4f83949e23f52fececce")), buy_token: ByteArray(hex!("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee")), kind: OrderKind::Sell, @@ -1958,10 +1949,10 @@ mod tests { insert_order(&mut db, &order).await.unwrap(); - crate::order_execution::save(&mut db, &order.uid, 1, None, Some(&bigdecimal(1))) + crate::order_execution::save(&mut db, &order.uid, 1, None) .await .unwrap(); - crate::order_execution::save(&mut db, &order.uid, 42, None, Some(&bigdecimal(42))) + crate::order_execution::save(&mut db, &order.uid, 42, None) .await .unwrap(); @@ -2003,7 +1994,6 @@ mod tests { assert_eq!( executions, vec![OrderExecution { - executed_solver_fee: Some(bigdecimal(42)), kind: OrderKind::Sell, class: OrderClass::Limit, sell_amount: bigdecimal(1), diff --git a/crates/model/src/solver_competition.rs b/crates/model/src/solver_competition.rs index 9ea9f59a4d..203fa0be91 100644 --- a/crates/model/src/solver_competition.rs +++ b/crates/model/src/solver_competition.rs @@ -45,9 +45,7 @@ pub struct Transaction { #[derive(Clone, Debug, Default, Deserialize, Serialize)] pub struct Execution { #[serde_as(as = "Option")] - pub surplus_fee: Option, - #[serde_as(as = "HexOrDecimalU256")] - pub solver_fee: U256, + pub executed_fee: Option, } /// Stored directly in the database and turned into SolverCompetitionAPI for the diff --git a/crates/orderbook/src/database/orders.rs b/crates/orderbook/src/database/orders.rs index 928536aae3..11fa5e0cc0 100644 --- a/crates/orderbook/src/database/orders.rs +++ b/crates/orderbook/src/database/orders.rs @@ -568,8 +568,7 @@ mod tests { ethflow_data: None, onchain_user: None, onchain_placement_error: None, - executed_surplus_fee: Default::default(), - executed_solver_fee: Default::default(), + executed_fee: Default::default(), full_app_data: Default::default(), }; diff --git a/crates/orderbook/src/database/solver_competition.rs b/crates/orderbook/src/database/solver_competition.rs index ca1f541e5d..e6dd17666c 100644 --- a/crates/orderbook/src/database/solver_competition.rs +++ b/crates/orderbook/src/database/solver_competition.rs @@ -40,13 +40,15 @@ impl SolverCompetitionStoring for Postgres { .context("upsert_auction_transaction")?; for (order, execution) in request.executions { - let surplus_fee = execution.surplus_fee.as_ref().map(u256_to_big_decimal); database::order_execution::save( &mut ex, &ByteArray(order.0), request.auction, - surplus_fee.as_ref(), - Some(&u256_to_big_decimal(&execution.solver_fee)), + execution + .executed_fee + .as_ref() + .map(u256_to_big_decimal) + .as_ref(), ) .await .context("order_execution::save")?; diff --git a/crates/shared/src/db_order_conversions.rs b/crates/shared/src/db_order_conversions.rs index d87850e2c7..ed837452fb 100644 --- a/crates/shared/src/db_order_conversions.rs +++ b/crates/shared/src/db_order_conversions.rs @@ -68,6 +68,13 @@ pub fn full_order_into_model_order(order: database::orders::FullOrder) -> Result // subsidized one which we don't want. OrderClass::Limit(_) | OrderClass::Market => full_fee_amount, }; + // Executed fees should be read from the order_execution table, but for + // backwards compatibility, for non-limit orders we use the sum of fees from + // the order table. + let executed_fee_amount = match class.is_limit() { + true => &order.executed_fee, + false => &order.sum_fee, + }; let metadata = OrderMetadata { creation_date: order.creation_timestamp, @@ -85,7 +92,7 @@ pub fn full_order_into_model_order(order: database::orders::FullOrder) -> Result .context( "executed sell amount before fees does not fit in a u256", )?, - executed_fee_amount: big_decimal_to_u256(&order.sum_fee) + executed_fee_amount: big_decimal_to_u256(executed_fee_amount) .context("executed fee amount is not a valid u256")?, invalidated: order.invalidated, status, @@ -208,7 +215,7 @@ pub fn order_class_from(order: &FullOrderDb) -> OrderClass { DbOrderClass::Market => OrderClass::Market, DbOrderClass::Liquidity => OrderClass::Liquidity, DbOrderClass::Limit => OrderClass::Limit(LimitOrderClass { - executed_surplus_fee: big_decimal_to_u256(&order.executed_surplus_fee).expect( + executed_surplus_fee: big_decimal_to_u256(&order.executed_fee).expect( "executed fees can't exceed sell_token amount which definitely fits into U256", ), }), diff --git a/crates/solver/src/driver.rs b/crates/solver/src/driver.rs index b65c6544c3..96ea15ad16 100644 --- a/crates/solver/src/driver.rs +++ b/crates/solver/src/driver.rs @@ -420,8 +420,10 @@ impl Driver { .user_trades() .map(|trade| { let execution = Execution { - surplus_fee: trade.surplus_fee(), - solver_fee: trade.solver_fee, + executed_fee: match trade.order.solver_determines_fee() { + true => None, + false => Some(trade.solver_fee), + }, }; (trade.order.metadata.uid, execution) }) diff --git a/crates/solver/src/settlement.rs b/crates/solver/src/settlement.rs index e9a5a67155..95f5530f52 100644 --- a/crates/solver/src/settlement.rs +++ b/crates/solver/src/settlement.rs @@ -24,6 +24,8 @@ pub struct Trade { pub order: Order, pub executed_amount: U256, /// The fee amount used for objective value computations. + /// Taken either from the order as user_fee or from the solver as + /// solver_fee. pub solver_fee: U256, } diff --git a/database/sql/V057__update_order_execution.sql b/database/sql/V057__update_order_execution.sql new file mode 100644 index 0000000000..4153c72401 --- /dev/null +++ b/database/sql/V057__update_order_execution.sql @@ -0,0 +1,12 @@ +-- solver_fee was added in V049__add_full_fee_amount.sql +-- Limit order's fee is no longer calculated beforehand, but provided by the solvers at the time of solving. However, under colocation, this fee is not reported to the autopilot but calculated from the onchain calldata. + +ALTER TABLE order_execution + DROP COLUMN solver_fee; + +-- Rename surplus_fee to executed_fee as it will represent the general fee paid to the solvers due to network fees. +-- For market orders, this fee can be fetched from the orders table (but we can keep the duplicate here for completness). +-- For limit orders, fee is expected to be calculated after the transaction mined, and then this field will be updated from autopilot. + +ALTER TABLE order_execution + RENAME surplus_fee TO executed_fee;