Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ON HOLD] Refactor order_execution table #2104

Closed
wants to merge 1 commit into from
Closed

Conversation

sunce86
Copy link
Contributor

@sunce86 sunce86 commented Nov 30, 2023

Description

Fixes #2100

Reviewing should be started by checking the changes in the order_execution table.
This table contains two columns for fees: surplus_fee and solver_fee, which are populated in a several, very non-understandable ways.

Surplus fee

surplus_fee refers to the fee determined by solvers at the time of solving, exclusively for limit orders. Then we adjust the clearing prices of the settlement to take into consideration this fee (so this is something observable from the calldata itself, we don't need to store at it the time of solving).
Before colocation, order_execution::surplus_fee was populated from solver crate at the time of execution, but we later override it in autopilot (which has a background task for updating the surplus_fee by observing onchain settlement).
After colocation, autopilot set's surplus_fee to None, during execution, but populates it after in the mentioned background task.

Solver fee

solver_fee was added to fix the bug we had for limit orders, where clients of our database were reading the surplus_fee field for limit orders when the surplus_fee was not calculated by solvers but the autopilot, iteratively, and was known upfront (before solving). In the meantime we removed that functionality, and continued using this field as a fee for market orders (which is known upfront, constant and already readable from orders table).

What now?

This PR suggests completely dropping the terminology surplus_fee and solver_fee and merging those into executed_fee.
We rename the order_execution::surplus_fee to order_execution::executed_fee (so we keep the data in that column) and we drop order_execution::solver_fee, but:

  1. For future trades, we can store the fee for market orders into executed_fee, for completness.
  2. For history trades, for backward compatibility, we fallback to reading from orders table. (I might consider executing the script on database to copy that data so we can drop the backward compatibility hack).

How to test

TODO! Since the change is tedious, will need to triple check everything works, but for now the idea seems legit.

@sunce86 sunce86 requested a review from a team as a code owner November 30, 2023 18:34
Copy link
Contributor

@fleupold fleupold left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is indeed very confusing (great job working through all the code paths). I like the proposal of a combined value!

I'd like to explore the idea of dropping fee reporting entirely from the autopilot/driver and have it be purely based on settlement even indexing. Do you think that would be possible and simplify things further?

@@ -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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this struct even needed in the co-located world? Can't all fees (fixed order fee as well at the surplus fee) be determined by parsing the settlement in the end? Would this simplify the code paths?

/// is finalized).
Solver(U256),
UserFee(U256),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: it's not really UserDetermined, but rather OrderDetermined?

How about ExecutedFee:Signed vs ExecutedFee::Surplus ?

@@ -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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Update fee for orders for which is not calculated yet.
// Update fee for orders for which it is not calculated yet.

@@ -138,15 +137,13 @@ impl From<(Address, U256, Bytes<Vec<u8>>)> for DecodedInteraction {
#[derive(Debug, Clone)]
pub struct OrderExecution {
pub order_uid: OrderUid,
pub executed_solver_fee: Option<U256>,
pub executed_fee: Option<U256>,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does this need to be Option?

Copy link
Contributor

@MartinquaXD MartinquaXD Dec 5, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like on_settlement_event_updater (which updates the required DB entries when a settlement gets indexed) requires 2 pieces of information:

  1. the full (signed) fee amount collected in total by the settlement
  2. all OrderExecutions that don't have a fee assigned to them yet

These pieces of information can currently be served by the same function Postgres::order_executions_for_tx() which returns all OrderExecutions (with and without a signed fee). For 1 we currently sum all the fees which are already populated and for 2 we use only the executions where executed_fee.is_none().

So refactoring this field to be non-optional might make other code weirder.

While looking into that I stumbled over this code. Could somebody give that a sanity check. My current understanding is that this code is correct because it should only contain the fees that were signed upfront by the user because those are the only fees that are part of the objective value computation. But because fees have become so complicated since we introduced fees that were not signed upfront I think checking that logic once more might be good.

@@ -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<BigDecimal>,
// Uknown for limit orders before the setlement is finalized.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Uknown for limit orders before the setlement is finalized.
// Unknown for limit orders before the settlement is finalized.

Comment on lines +71 to +73
// 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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can make the migration of values part of the flyway migration below.

Comment on lines +4 to +12
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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In case we need to roll back this release, these changes would not be backwards compatible. Would it be possible to create a now column and move the values from the two previous columns into it, and then follow up with another migration a few weeks later, when things have stabilized?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could do that, but that would require the change in the storing/retrieving functions for this table right? Since we need to also continue populating this new column after the migration. Need to think about it.

@sunce86
Copy link
Contributor Author

sunce86 commented Dec 2, 2023

I'd like to explore the idea of dropping fee reporting entirely from the autopilot/driver and have it be purely based on settlement even indexing. Do you think that would be possible and simplify things further?

Yes, that would be the ultimate goal. Based on the types of fees we currently have and how they are calculated, order_execution table should be able to support reindexing and not depend on some specific data at the time of execution.

Comment on lines -48 to -50
pub surplus_fee: Option<U256>,
#[serde_as(as = "HexOrDecimalU256")]
pub solver_fee: U256,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there anybody consuming these fields? I.e. is this a relevant breaking change?

Comment on lines +4 to +5
ALTER TABLE order_execution
DROP COLUMN solver_fee;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't we have to add the data that currently exists in this column in the surplus_fee column (that gets renamed to executed_fee)?
Otherwise we'd lose data which does not seem right.

@sunce86 sunce86 changed the title Refactor order_execution table [ON HOLD] Refactor order_execution table Dec 16, 2023
@sunce86
Copy link
Contributor Author

sunce86 commented Dec 29, 2023

Closing in favor #2190

This only change this PR has different from #2190 is dropping the field in the database, which I will do at the end of fee cleanup.

@sunce86 sunce86 closed this Dec 29, 2023
@github-actions github-actions bot locked and limited conversation to collaborators Dec 29, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

chore: Drop solver_fee column from order_execution table
4 participants