Skip to content

Commit

Permalink
Executed total fee token (#2966)
Browse files Browse the repository at this point in the history
# Description
Implements (1) and (2) from
#2963.
Also, renames `surplus_fee` to `executed_fee` in database.

# Changes
<!-- List of detailed changes (how the change is accomplished) -->

- [ ] 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 #2963.

## How to test
Existing e2e tests. Observed values during the tests.
  • Loading branch information
sunce86 committed Dec 25, 2024
1 parent 1451574 commit cfc787d
Show file tree
Hide file tree
Showing 18 changed files with 194 additions and 87 deletions.
28 changes: 24 additions & 4 deletions crates/autopilot/src/domain/settlement/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,27 @@ impl Settlement {
}

/// Per order fees breakdown. Contains all orders from the settlement
pub fn fee_breakdown(&self) -> HashMap<domain::OrderUid, Option<trade::FeeBreakdown>> {
pub fn fee_breakdown(&self) -> HashMap<domain::OrderUid, trade::FeeBreakdown> {
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()
}

Expand Down Expand Up @@ -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!(
Expand Down Expand Up @@ -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()
Expand Down
6 changes: 5 additions & 1 deletion crates/autopilot/src/domain/settlement/trade/math.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<eth::SellTokenAmount, Error> {
pub fn fee_in_sell_token(&self) -> Result<eth::Asset, Error> {
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
Expand Down
11 changes: 9 additions & 2 deletions crates/autopilot/src/domain/settlement/trade/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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<ExecutedProtocolFee>,
}
Expand Down
29 changes: 12 additions & 17 deletions crates/autopilot/src/infra/persistence/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::<Vec<_>>()
})
.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::<Vec<_>>(),
)
.await?;
}
Expand Down
3 changes: 2 additions & 1 deletion crates/database/src/jit_orders.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
"#;

Expand Down
39 changes: 26 additions & 13 deletions crates/database/src/order_execution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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()),
Expand Down
47 changes: 36 additions & 11 deletions crates/database/src/orders.rs
Original file line number Diff line number Diff line change
Expand Up @@ -493,7 +493,8 @@ pub struct FullOrder {
pub ethflow_data: Option<(Option<TransactionHash>, i64)>,
pub onchain_user: Option<Address>,
pub onchain_placement_error: Option<OnchainOrderPlacementError>,
pub executed_surplus_fee: BigDecimal,
pub executed_fee: BigDecimal,
pub executed_fee_token: Address,
pub full_app_data: Option<Vec<u8>>,
}

Expand Down Expand Up @@ -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
"#;

Expand Down Expand Up @@ -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},
Expand Down Expand Up @@ -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]
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down
2 changes: 1 addition & 1 deletion crates/e2e/tests/e2e/ethflow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ async fn eth_flow_tx(web3: Web3) {
.get_order(&ethflow_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();

Expand Down
4 changes: 2 additions & 2 deletions crates/e2e/tests/e2e/limit_orders.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
2 changes: 1 addition & 1 deletion crates/e2e/tests/e2e/partial_fill.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion crates/e2e/tests/e2e/partially_fillable_pool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
};
Expand Down
Loading

0 comments on commit cfc787d

Please sign in to comment.