From cfc787d6b37ce3dd8889f9a9942070a71d072dc1 Mon Sep 17 00:00:00 2001 From: Dusan Stanivukovic Date: Tue, 24 Dec 2024 17:01:58 +0100 Subject: [PATCH] Executed total fee token (#2966) # Description Implements (1) and (2) from https://github.com/cowprotocol/services/issues/2963. Also, renames `surplus_fee` to `executed_fee` in database. # Changes - [ ] Added new field `order_execution.executed_fee_token` to database - [ ] Populated this field with order sell token, otherwise default value is 0x0 - [ ] Token is exposed over API the same way as `executed_fee`. Note: all places where sell token needs to be substituted with surplus token are marked with `TODO surplus token` and this will be tackled as part of (3) from https://github.com/cowprotocol/services/issues/2963. ## How to test Existing e2e tests. Observed values during the tests. --- crates/autopilot/src/domain/settlement/mod.rs | 28 +++++++++-- .../src/domain/settlement/trade/math.rs | 6 ++- .../src/domain/settlement/trade/mod.rs | 11 ++++- crates/autopilot/src/infra/persistence/mod.rs | 29 +++++------- crates/database/src/jit_orders.rs | 3 +- crates/database/src/order_execution.rs | 39 ++++++++++----- crates/database/src/orders.rs | 47 ++++++++++++++----- crates/e2e/tests/e2e/ethflow.rs | 2 +- crates/e2e/tests/e2e/limit_orders.rs | 4 +- crates/e2e/tests/e2e/partial_fill.rs | 2 +- .../e2e/tests/e2e/partially_fillable_pool.rs | 2 +- crates/e2e/tests/e2e/protocol_fee.rs | 45 ++++++++---------- crates/model/src/order.rs | 7 +++ crates/orderbook/openapi.yml | 12 ++++- crates/orderbook/src/database/orders.rs | 10 ++-- crates/shared/src/db_order_conversions.rs | 7 ++- database/README.md | 3 +- database/sql/V076__total_fee_token.sql | 24 ++++++++++ 18 files changed, 194 insertions(+), 87 deletions(-) create mode 100644 database/sql/V076__total_fee_token.sql diff --git a/crates/autopilot/src/domain/settlement/mod.rs b/crates/autopilot/src/domain/settlement/mod.rs index b910a9ceb5..2a47bd4c75 100644 --- a/crates/autopilot/src/domain/settlement/mod.rs +++ b/crates/autopilot/src/domain/settlement/mod.rs @@ -89,10 +89,27 @@ impl Settlement { } /// Per order fees breakdown. Contains all orders from the settlement - pub fn fee_breakdown(&self) -> HashMap> { + pub fn fee_breakdown(&self) -> HashMap { self.trades .iter() - .map(|trade| (*trade.uid(), trade.fee_breakdown(&self.auction).ok())) + .map(|trade| { + let fee_breakdown = trade.fee_breakdown(&self.auction).unwrap_or_else(|err| { + tracing::warn!( + ?err, + trade = %trade.uid(), + "possible incomplete fee breakdown calculation", + ); + trade::FeeBreakdown { + total: eth::Asset { + // TODO surplus token + token: trade.sell_token(), + amount: num::zero(), + }, + protocol: vec![], + } + }); + (*trade.uid(), fee_breakdown) + }) .collect() } @@ -329,7 +346,7 @@ mod tests { trade.surplus_in_ether(&auction.prices).unwrap().0, eth::U256::from(52937525819789126u128) ); - // fee read from "executedSurplusFee" https://api.cow.fi/mainnet/api/v1/orders/0x10dab31217bb6cc2ace0fe601c15d342f7626a1ee5ef0495449800e73156998740a50cf069e992aa4536211b23f286ef88752187ffffffff + // fee read from "executedFee" https://api.cow.fi/mainnet/api/v1/orders/0x10dab31217bb6cc2ace0fe601c15d342f7626a1ee5ef0495449800e73156998740a50cf069e992aa4536211b23f286ef88752187ffffffff // but not equal to 6890975030480504 anymore, since after this tx we switched to // convert the fee from surplus token directly to ether assert_eq!( @@ -810,7 +827,10 @@ mod tests { let jit_trade = super::trade::Trade::new(transaction.trades[1].clone(), &auction, 0); assert_eq!(jit_trade.fee_in_ether(&auction.prices).unwrap().0, 0.into()); assert_eq!(jit_trade.score(&auction).unwrap().0, 0.into()); - assert_eq!(jit_trade.fee_breakdown(&auction).unwrap().total.0, 0.into()); + assert_eq!( + jit_trade.fee_breakdown(&auction).unwrap().total.amount.0, + 0.into() + ); assert!(jit_trade .fee_breakdown(&auction) .unwrap() diff --git a/crates/autopilot/src/domain/settlement/trade/math.rs b/crates/autopilot/src/domain/settlement/trade/math.rs index 9a1d40c093..5ff42f01d5 100644 --- a/crates/autopilot/src/domain/settlement/trade/math.rs +++ b/crates/autopilot/src/domain/settlement/trade/math.rs @@ -134,9 +134,13 @@ impl Trade { /// Total fee (protocol fee + network fee). Equal to a surplus difference /// before and after applying the fees. - pub fn fee_in_sell_token(&self) -> Result { + pub fn fee_in_sell_token(&self) -> Result { let fee = self.fee()?; self.fee_into_sell_token(fee.amount) + .map(|amount| eth::Asset { + token: self.sell.token, + amount: amount.into(), + }) } /// Total fee (protocol fee + network fee). Equal to a surplus difference diff --git a/crates/autopilot/src/domain/settlement/trade/mod.rs b/crates/autopilot/src/domain/settlement/trade/mod.rs index 6989df1562..14b7261917 100644 --- a/crates/autopilot/src/domain/settlement/trade/mod.rs +++ b/crates/autopilot/src/domain/settlement/trade/mod.rs @@ -75,6 +75,13 @@ impl Trade { Ok(FeeBreakdown { total, protocol }) } + pub fn sell_token(&self) -> eth::TokenAddress { + match self { + Self::Fulfillment(trade) => trade.sell.token, + Self::Jit(trade) => trade.sell.token, + } + } + pub fn new(trade: transaction::EncodedTrade, auction: &super::Auction, created: u32) -> Self { if auction.orders.contains_key(&trade.uid) { Trade::Fulfillment(Fulfillment { @@ -161,8 +168,8 @@ pub struct Jit { #[derive(Debug, Clone)] pub struct FeeBreakdown { /// Total fee the trade was charged (network fee + protocol fee) - // TODO: express in surplus token - pub total: eth::SellTokenAmount, + // TODO surplus token + pub total: eth::Asset, /// Breakdown of protocol fees. pub protocol: Vec, } diff --git a/crates/autopilot/src/infra/persistence/mod.rs b/crates/autopilot/src/infra/persistence/mod.rs index 3fbd7e2140..ce4654df77 100644 --- a/crates/autopilot/src/infra/persistence/mod.rs +++ b/crates/autopilot/src/infra/persistence/mod.rs @@ -701,28 +701,23 @@ impl Persistence { .await; for (order, order_fee) in fee_breakdown { - let total_fee = order_fee - .as_ref() - .map(|fee| u256_to_big_decimal(&fee.total.0)) - .unwrap_or_default(); - let executed_protocol_fees = order_fee - .map(|fee| { - fee.protocol - .into_iter() - .map(|executed| Asset { - token: ByteArray(executed.fee.token.0 .0), - amount: u256_to_big_decimal(&executed.fee.amount.0), - }) - .collect::>() - }) - .unwrap_or_default(); database::order_execution::save( &mut ex, &ByteArray(order.0), auction_id, block_number, - &total_fee, - &executed_protocol_fees, + Asset { + token: ByteArray(order_fee.total.token.0 .0), + amount: u256_to_big_decimal(&order_fee.total.amount.0), + }, + &order_fee + .protocol + .into_iter() + .map(|executed| Asset { + token: ByteArray(executed.fee.token.0 .0), + amount: u256_to_big_decimal(&executed.fee.amount.0), + }) + .collect::>(), ) .await?; } diff --git a/crates/database/src/jit_orders.rs b/crates/database/src/jit_orders.rs index 12f79a0e6d..c10de8fe42 100644 --- a/crates/database/src/jit_orders.rs +++ b/crates/database/src/jit_orders.rs @@ -31,7 +31,8 @@ ARRAY[]::record[] AS post_interactions, NULL AS ethflow_data, NULL AS onchain_user, NULL 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(executed_fee) FROM order_execution oe WHERE oe.order_uid = o.uid), 0) as executed_fee, +COALESCE((SELECT executed_fee_token FROM order_execution oe WHERE oe.order_uid = o.uid), o.sell_token) as executed_fee_token, -- TODO surplus token NULL AS full_app_data "#; diff --git a/crates/database/src/order_execution.rs b/crates/database/src/order_execution.rs index 18d81a7cbc..21059b272f 100644 --- a/crates/database/src/order_execution.rs +++ b/crates/database/src/order_execution.rs @@ -18,25 +18,26 @@ pub async fn save( order: &OrderUid, auction: AuctionId, block_number: i64, - executed_fee: &BigDecimal, - executed_protocol_fees: &[Asset], + executed_fee: Asset, + protocol_fees: &[Asset], ) -> Result<(), sqlx::Error> { - let (protocol_fee_tokens, protocol_fee_amounts): (Vec<_>, Vec<_>) = executed_protocol_fees + let (protocol_fee_tokens, protocol_fee_amounts): (Vec<_>, Vec<_>) = protocol_fees .iter() .map(|entry| (entry.token, entry.amount.clone())) .unzip(); const QUERY: &str = r#" -INSERT INTO order_execution (order_uid, auction_id, reward, surplus_fee, block_number, protocol_fee_tokens, protocol_fee_amounts) -VALUES ($1, $2, $3, $4, $5, $6, $7) +INSERT INTO order_execution (order_uid, auction_id, reward, executed_fee, executed_fee_token, block_number, protocol_fee_tokens, protocol_fee_amounts) +VALUES ($1, $2, $3, $4, $5, $6, $7, $8) ON CONFLICT (order_uid, auction_id) -DO UPDATE SET reward = $3, surplus_fee = $4, block_number = $5, protocol_fee_tokens = $6, protocol_fee_amounts = $7 +DO UPDATE SET reward = $3, executed_fee = $4, executed_fee_token = $5, block_number = $6, protocol_fee_tokens = $7, protocol_fee_amounts = $8 ;"#; sqlx::query(QUERY) .bind(order) .bind(auction) .bind(0.) // reward is deprecated but saved for historical analysis - .bind(Some(executed_fee)) + .bind(executed_fee.amount) + .bind(executed_fee.token) .bind(block_number) .bind(protocol_fee_tokens) .bind(protocol_fee_amounts) @@ -126,17 +127,29 @@ mod tests { &Default::default(), 1, 0, - &Default::default(), + Asset { + amount: BigDecimal::from(3), + token: Default::default(), + }, protocol_fees.as_slice(), ) .await .unwrap(); - // save entry without protocol fees (simulate case when we are still not - // calculating them) - save(&mut db, &Default::default(), 2, 0, &Default::default(), &[]) - .await - .unwrap(); + // save entry for an order without protocol fees + save( + &mut db, + &Default::default(), + 2, + 0, + Asset { + amount: BigDecimal::from(3), + token: Default::default(), + }, + &[], + ) + .await + .unwrap(); let keys: Vec<(AuctionId, OrderUid)> = vec![ (1, Default::default()), diff --git a/crates/database/src/orders.rs b/crates/database/src/orders.rs index ebcc249abe..efb67b32bd 100644 --- a/crates/database/src/orders.rs +++ b/crates/database/src/orders.rs @@ -493,7 +493,8 @@ 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_fee: BigDecimal, + pub executed_fee_token: Address, pub full_app_data: Option>, } @@ -570,7 +571,8 @@ 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(executed_fee) FROM order_execution oe WHERE oe.order_uid = o.uid), 0) as executed_fee, +COALESCE((SELECT executed_fee_token FROM order_execution oe WHERE oe.order_uid = o.uid), o.sell_token) as executed_fee_token, -- TODO surplus token (SELECT full_app_data FROM app_data ad WHERE o.app_data = ad.contract_app_data LIMIT 1) as full_app_data "#; @@ -820,6 +822,7 @@ mod tests { events::{Event, EventIndex, Invalidation, PreSignature, Settlement, Trade}, onchain_broadcasted_orders::{insert_onchain_order, OnchainOrderPlacement}, onchain_invalidations::insert_onchain_invalidation, + order_execution::Asset, PgTransaction, }, bigdecimal::num_bigint::{BigInt, ToBigInt}, @@ -1917,18 +1920,28 @@ mod tests { .await .unwrap() .unwrap(); - assert_eq!(order.executed_surplus_fee, fee); + assert_eq!(order.executed_fee, fee); let fee: BigDecimal = 1.into(); - crate::order_execution::save(&mut db, &order_uid, 1, 0, &fee, &[]) - .await - .unwrap(); + crate::order_execution::save( + &mut db, + &order_uid, + 1, + 0, + Asset { + amount: fee.clone(), + token: Default::default(), + }, + &[], + ) + .await + .unwrap(); let order = single_full_order(&mut db, &order_uid) .await .unwrap() .unwrap(); - assert_eq!(order.executed_surplus_fee, fee); + assert_eq!(order.executed_fee, fee); } #[tokio::test] @@ -2042,7 +2055,10 @@ mod tests { &ByteArray([1u8; 56]), 1, 1, - &BigDecimal::from(1), + Asset { + amount: BigDecimal::from(1), + token: Default::default(), + }, Default::default(), ) .await @@ -2052,7 +2068,10 @@ mod tests { &ByteArray([1u8; 56]), 2, 2, - &BigDecimal::from(2), + Asset { + amount: BigDecimal::from(2), + token: Default::default(), + }, Default::default(), ) .await @@ -2062,7 +2081,10 @@ mod tests { &ByteArray([1u8; 56]), 3, 0, - &BigDecimal::from(4), + Asset { + amount: BigDecimal::from(4), + token: Default::default(), + }, Default::default(), ) .await @@ -2072,7 +2094,10 @@ mod tests { &ByteArray([3u8; 56]), 2, 3, - &BigDecimal::from(4), + Asset { + amount: BigDecimal::from(4), + token: Default::default(), + }, Default::default(), ) .await diff --git a/crates/e2e/tests/e2e/ethflow.rs b/crates/e2e/tests/e2e/ethflow.rs index 3a8b0e3591..74a75c54b1 100644 --- a/crates/e2e/tests/e2e/ethflow.rs +++ b/crates/e2e/tests/e2e/ethflow.rs @@ -118,7 +118,7 @@ async fn eth_flow_tx(web3: Web3) { .get_order(ðflow_order.uid(onchain.contracts()).await) .await .unwrap(); - order.metadata.executed_surplus_fee > U256::zero() + order.metadata.executed_fee > U256::zero() }; wait_for_condition(TIMEOUT, fee_charged).await.unwrap(); diff --git a/crates/e2e/tests/e2e/limit_orders.rs b/crates/e2e/tests/e2e/limit_orders.rs index 94eb7e9e21..4f7ed21714 100644 --- a/crates/e2e/tests/e2e/limit_orders.rs +++ b/crates/e2e/tests/e2e/limit_orders.rs @@ -445,9 +445,9 @@ async fn two_limit_orders_multiple_winners_test(web3: Web3) { assert_eq!(competition.transaction_hashes.len(), 2); // Verify that settlement::Observed properly handled events let order_a_settled = services.get_order(&uid_a).await.unwrap(); - assert!(order_a_settled.metadata.executed_surplus_fee > 0.into()); + assert!(order_a_settled.metadata.executed_fee > 0.into()); let order_b_settled = services.get_order(&uid_b).await.unwrap(); - assert!(order_b_settled.metadata.executed_surplus_fee > 0.into()); + assert!(order_b_settled.metadata.executed_fee > 0.into()); } async fn too_many_limit_orders_test(web3: Web3) { diff --git a/crates/e2e/tests/e2e/partial_fill.rs b/crates/e2e/tests/e2e/partial_fill.rs index 1ffcb2540d..156e0ff4f1 100644 --- a/crates/e2e/tests/e2e/partial_fill.rs +++ b/crates/e2e/tests/e2e/partial_fill.rs @@ -95,7 +95,7 @@ async fn test(web3: Web3) { let settlement_event_processed = || async { onchain.mint_block().await; let order = services.get_order(&uid).await.unwrap(); - order.metadata.executed_surplus_fee > U256::zero() + order.metadata.executed_fee > U256::zero() }; wait_for_condition(TIMEOUT, settlement_event_processed) .await diff --git a/crates/e2e/tests/e2e/partially_fillable_pool.rs b/crates/e2e/tests/e2e/partially_fillable_pool.rs index 84b3f0bd72..18a1adcf07 100644 --- a/crates/e2e/tests/e2e/partially_fillable_pool.rs +++ b/crates/e2e/tests/e2e/partially_fillable_pool.rs @@ -118,7 +118,7 @@ async fn test(web3: Web3) { let metadata_updated = || async { let order = services.get_order(&uid).await.unwrap(); onchain.mint_block().await; - !order.metadata.executed_surplus_fee.is_zero() + !order.metadata.executed_fee.is_zero() && order.metadata.executed_buy_amount != Default::default() && order.metadata.executed_sell_amount != Default::default() }; diff --git a/crates/e2e/tests/e2e/protocol_fee.rs b/crates/e2e/tests/e2e/protocol_fee.rs index ffd65e9d26..5e8278c26b 100644 --- a/crates/e2e/tests/e2e/protocol_fee.rs +++ b/crates/e2e/tests/e2e/protocol_fee.rs @@ -278,7 +278,7 @@ async fn combined_protocol_fees(web3: Web3) { services .get_order(uid) .await - .is_ok_and(|order| !order.metadata.executed_surplus_fee.is_zero()) + .is_ok_and(|order| !order.metadata.executed_fee.is_zero()) }), ) .await @@ -294,39 +294,38 @@ async fn combined_protocol_fees(web3: Web3) { .get_order(&market_price_improvement_uid) .await .unwrap(); - let market_executed_surplus_fee_in_buy_token = - surplus_fee_in_buy_token(&market_price_improvement_order, &market_quote_after.quote); + let market_executed_fee_in_buy_token = + fee_in_buy_token(&market_price_improvement_order, &market_quote_after.quote); let market_quote_diff = market_quote_after .quote .buy_amount .saturating_sub(market_quote_before.quote.buy_amount); // see `market_price_improvement_policy.factor`, which is 0.3 - assert!(market_executed_surplus_fee_in_buy_token >= market_quote_diff * 3 / 10); + assert!(market_executed_fee_in_buy_token >= market_quote_diff * 3 / 10); let partner_fee_order = services.get_order(&partner_fee_order_uid).await.unwrap(); - let partner_fee_executed_surplus_fee_in_buy_token = - surplus_fee_in_buy_token(&partner_fee_order, &partner_fee_quote_after.quote); + let partner_fee_executed_fee_in_buy_token = + fee_in_buy_token(&partner_fee_order, &partner_fee_quote_after.quote); assert!( // see `--fee-policy-max-partner-fee` autopilot config argument, which is 0.02 - partner_fee_executed_surplus_fee_in_buy_token - >= partner_fee_quote.quote.buy_amount * 2 / 100 + partner_fee_executed_fee_in_buy_token >= partner_fee_quote.quote.buy_amount * 2 / 100 ); let limit_quote_diff = partner_fee_quote_after .quote .buy_amount .saturating_sub(partner_fee_order.data.buy_amount); // see `limit_surplus_policy.factor`, which is 0.3 - assert!(partner_fee_executed_surplus_fee_in_buy_token >= limit_quote_diff * 3 / 10); + assert!(partner_fee_executed_fee_in_buy_token >= limit_quote_diff * 3 / 10); let limit_surplus_order = services.get_order(&limit_surplus_order_uid).await.unwrap(); - let limit_executed_surplus_fee_in_buy_token = - surplus_fee_in_buy_token(&limit_surplus_order, &limit_quote_after.quote); + let limit_executed_fee_in_buy_token = + fee_in_buy_token(&limit_surplus_order, &limit_quote_after.quote); let limit_quote_diff = limit_quote_after .quote .buy_amount .saturating_sub(limit_surplus_order.data.buy_amount); // see `limit_surplus_policy.factor`, which is 0.3 - assert!(limit_executed_surplus_fee_in_buy_token >= limit_quote_diff * 3 / 10); + assert!(limit_executed_fee_in_buy_token >= limit_quote_diff * 3 / 10); let [market_order_token_balance, limit_order_token_balance, partner_fee_order_token_balance] = futures::future::try_join_all( @@ -345,16 +344,10 @@ async fn combined_protocol_fees(web3: Web3) { .unwrap() .try_into() .expect("Expected exactly four elements"); + assert_approximately_eq!(market_executed_fee_in_buy_token, market_order_token_balance); + assert_approximately_eq!(limit_executed_fee_in_buy_token, limit_order_token_balance); assert_approximately_eq!( - market_executed_surplus_fee_in_buy_token, - market_order_token_balance - ); - assert_approximately_eq!( - limit_executed_surplus_fee_in_buy_token, - limit_order_token_balance - ); - assert_approximately_eq!( - partner_fee_executed_surplus_fee_in_buy_token, + partner_fee_executed_fee_in_buy_token, partner_fee_order_token_balance ); } @@ -387,8 +380,8 @@ async fn get_quote( services.submit_quote("e_request).await } -fn surplus_fee_in_buy_token(order: &Order, quote: &OrderQuote) -> U256 { - order.metadata.executed_surplus_fee * quote.buy_amount / quote.sell_amount +fn fee_in_buy_token(order: &Order, quote: &OrderQuote) -> U256 { + order.metadata.executed_fee * quote.buy_amount / quote.sell_amount } fn sell_order_from_quote(quote: &OrderQuoteResponse) -> OrderCreation { @@ -517,13 +510,13 @@ async fn volume_fee_buy_order_test(web3: Web3) { let metadata_updated = || async { onchain.mint_block().await; let order = services.get_order(&uid).await.unwrap(); - !order.metadata.executed_surplus_fee.is_zero() + !order.metadata.executed_fee.is_zero() }; wait_for_condition(TIMEOUT, metadata_updated).await.unwrap(); let order = services.get_order(&uid).await.unwrap(); let fee_in_buy_token = quote.fee_amount * quote.buy_amount / quote.sell_amount; - assert!(order.metadata.executed_surplus_fee >= fee_in_buy_token + quote.sell_amount / 10); + assert!(order.metadata.executed_fee >= fee_in_buy_token + quote.sell_amount / 10); // Check settlement contract balance let balance_after = token_gno @@ -531,5 +524,5 @@ async fn volume_fee_buy_order_test(web3: Web3) { .call() .await .unwrap(); - assert_eq!(order.metadata.executed_surplus_fee, balance_after); + assert_eq!(order.metadata.executed_fee, balance_after); } diff --git a/crates/model/src/order.rs b/crates/model/src/order.rs index ec19f1caff..6aa3e4b78b 100644 --- a/crates/model/src/order.rs +++ b/crates/model/src/order.rs @@ -677,6 +677,9 @@ pub struct OrderMetadata { pub executed_fee_amount: U256, #[serde_as(as = "HexOrDecimalU256")] pub executed_surplus_fee: U256, + #[serde_as(as = "HexOrDecimalU256")] + pub executed_fee: U256, + pub executed_fee_token: H160, pub invalidated: bool, pub status: OrderStatus, #[serde(flatten)] @@ -1023,6 +1026,8 @@ mod tests { "appData": "0x6000000000000000000000000000000000000000000000000000000000000007", "feeAmount": "115792089237316195423570985008687907853269984665640564039457584007913129639935", "executedSurplusFee": "1", + "executedFee": "1", + "executedFeeToken": "0x000000000000000000000000000000000000000a", "fullFeeAmount": "115792089237316195423570985008687907853269984665640564039457584007913129639935", "solverFee": "115792089237316195423570985008687907853269984665640564039457584007913129639935", "kind": "buy", @@ -1054,6 +1059,8 @@ mod tests { executed_sell_amount_before_fees: 4.into(), executed_fee_amount: 1.into(), executed_surplus_fee: 1.into(), + executed_fee: 1.into(), + executed_fee_token: H160::from_low_u64_be(10), invalidated: true, status: OrderStatus::Open, settlement_contract: H160::from_low_u64_be(2), diff --git a/crates/orderbook/openapi.yml b/crates/orderbook/openapi.yml index 18e48c405f..aa4c2da3ac 100644 --- a/crates/orderbook/openapi.yml +++ b/crates/orderbook/openapi.yml @@ -951,7 +951,17 @@ components: description: Surplus fee that the limit order was executed with. allOf: - $ref: "#/components/schemas/BigUint" - nullable: true + nullable: false + executedFee: + description: Total fee charged for execution of the order. Contains network fee and protocol fees. + allOf: + - $ref: "#/components/schemas/BigUint" + nullable: false + executedFeeToken: + description: Token the executed fee was captured in. + allOf: + - $ref: "#/components/schemas/Address" + nullable: false fullAppData: description: > Full `appData`, which the contract-level `appData` is a hash of. See diff --git a/crates/orderbook/src/database/orders.rs b/crates/orderbook/src/database/orders.rs index 64bde48916..938389b210 100644 --- a/crates/orderbook/src/database/orders.rs +++ b/crates/orderbook/src/database/orders.rs @@ -601,8 +601,11 @@ fn full_order_into_model_order(order: FullOrder) -> Result { )?, executed_fee_amount: big_decimal_to_u256(&order.sum_fee) .context("executed fee amount is not a valid u256")?, - executed_surplus_fee: big_decimal_to_u256(&order.executed_surplus_fee) - .context("executed surplus fee is not a valid u256")?, + executed_surplus_fee: big_decimal_to_u256(&order.executed_fee) + .context("executed fee is not a valid u256")?, + executed_fee: big_decimal_to_u256(&order.executed_fee) + .context("executed fee is not a valid u256")?, + executed_fee_token: H160(order.executed_fee_token.0), invalidated: order.invalidated, status, is_liquidity_order: class == OrderClass::Liquidity, @@ -726,7 +729,8 @@ mod tests { ethflow_data: None, onchain_user: None, onchain_placement_error: None, - executed_surplus_fee: Default::default(), + executed_fee: Default::default(), + executed_fee_token: ByteArray([1; 20]), // TODO surplus token full_app_data: Default::default(), }; diff --git a/crates/shared/src/db_order_conversions.rs b/crates/shared/src/db_order_conversions.rs index 4bbdf8f86b..44e2ae3320 100644 --- a/crates/shared/src/db_order_conversions.rs +++ b/crates/shared/src/db_order_conversions.rs @@ -90,8 +90,11 @@ pub fn full_order_into_model_order(order: database::orders::FullOrder) -> Result )?, executed_fee_amount: big_decimal_to_u256(&order.sum_fee) .context("executed fee amount is not a valid u256")?, - executed_surplus_fee: big_decimal_to_u256(&order.executed_surplus_fee) - .context("executed surplus fee is not a valid u256")?, + executed_surplus_fee: big_decimal_to_u256(&order.executed_fee) + .context("executed fee is not a valid u256")?, + executed_fee: big_decimal_to_u256(&order.executed_fee) + .context("executed fee is not a valid u256")?, + executed_fee_token: H160(order.executed_fee_token.0), invalidated: order.invalidated, status, is_liquidity_order: class == OrderClass::Liquidity, diff --git a/database/README.md b/database/README.md index abb2c53b29..8b8980b95c 100644 --- a/database/README.md +++ b/database/README.md @@ -210,7 +210,8 @@ Protocol fee tokens/amounts are stored in the same order as fee policies in fee_ order\_uid | bytea | not null | which order this trade execution is related to auction\_id | bigint | not null | in which auction this trade was initiated reward | double | not null | revert adjusted solver rewards, deprecated in favor of [CIP-20](https://snapshot.org/#/cow.eth/proposal/0x2d3f9bd1ea72dca84b03e97dda3efc1f4a42a772c54bd2037e8b62e7d09a491f) - surplus\_fee | numeric | nullable | dynamic fee computed by the protocol that should get taken from the surplus of a trade, this value only applies and is set for fill-or-kill limit orders. + executed\_fee | numeric | not null | fee taken for execution of the trade + executed\_fee\_token | bytea | not null | token in which the executed fee is taken block\_number | bigint | not null | block in which the order was executed protocol\_fee\_tokens | bytea[] | not null | tokens in which the protocol fees are taken protocol\_fee\_amounts | numeric[] | not null | amounts of protocol fees taken, aligned protocol\_fee\_tokens array diff --git a/database/sql/V076__total_fee_token.sql b/database/sql/V076__total_fee_token.sql new file mode 100644 index 0000000000..0c767efff9 --- /dev/null +++ b/database/sql/V076__total_fee_token.sql @@ -0,0 +1,24 @@ +ALTER TABLE order_execution +RENAME COLUMN surplus_fee TO executed_fee; + +-- Add a new column to the order_execution table to store the executed fee token +ALTER TABLE order_execution +ADD COLUMN executed_fee_token bytea NOT NULL DEFAULT '\x0000000000000000000000000000000000000000'; + +-- Now populate existing rows with the sell token taken from the 'orders' table, or if it doesn't exist, try from 'jit_orders'. +UPDATE order_execution oe +SET executed_fee_token = COALESCE( + sub.sell_token, '\x0000000000000000000000000000000000000000' +) +FROM ( + SELECT o.uid, o.sell_token + FROM orders o + UNION + SELECT j.uid, j.sell_token + FROM jit_orders j + WHERE NOT EXISTS (SELECT 1 FROM orders WHERE orders.uid = j.uid) +) AS sub +WHERE oe.order_uid = sub.uid; + +-- Drop the default value for the executed_fee_token column +ALTER TABLE order_execution ALTER COLUMN executed_fee_token DROP DEFAULT;