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

[BLOCKED] Save executed fee in surplus token #3184

Open
wants to merge 7 commits into
base: main
Choose a base branch
from

Conversation

sunce86
Copy link
Contributor

@sunce86 sunce86 commented Dec 26, 2024

Blocked by frontend team. I need a confirmation that frontend team switched to using executed_fee field introduced in #2966

Description

This PR implements item (3) from #2963

Previously, executed fees were saved and displayed in the sell token. With this change, executed fees will now be saved and expressed in the surplus token.

This change applies to all orders settled after this PR is merged.

  • Historical entries will remain expressed in the sell token.
  • From now on, all fees (e.g., gas fees, protocol fees) will be consistently saved and expressed in the surplus token. 🥳

Follow-Up Tasks

Migrate Historical Fees to Surplus Token

I will manually execute a migration immediately after this PR is merged.

  • This migration will convert existing fee values from the sell token to the surplus token using UCP prices from solver competition.
  • The migration is safe because it effectively reverses the logic used to save fees in the sell token in the current implementation.

Fix Remaining Issue

After the migration, we need to update the following code reference:
https://github.com/cowprotocol/services/blob/main/crates/database/src/orders.rs#L575

  • Currently, the system will have a mix of fees saved in the sell token and surplus token.
  • Once all fees are migrated to the surplus token, we can finalize this fix

Changes

  • Save executed_fee in surplus token instead of in sell token

How to test

Expanded one unit test.
Existing e2e tests.

@sunce86 sunce86 self-assigned this Dec 26, 2024
@sunce86 sunce86 requested a review from a team as a code owner December 26, 2024 11:35
@squadgazzz
Copy link
Contributor

I will manually execute a migration immediately after this PR is merged.

Is this script ready? It would be worth posting it in the description under a spoiler. Also, I am not sure I understand how the old entries will be identified; the script will probably explain it.

@sunce86
Copy link
Contributor Author

sunce86 commented Dec 26, 2024

I will manually execute a migration immediately after this PR is merged.

Is this script ready? It would be worth posting it in the description under a spoiler. Also, I am not sure I understand how the old entries will be identified; the script will probably explain it.

Not yet ready, currently implementing it.

}

/// Total fee (protocol fee + network fee). Equal to a surplus difference
Copy link
Contributor

Choose a reason for hiding this comment

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

Silly question: what is the difference between network and gas fees in this context?

Copy link
Contributor Author

@sunce86 sunce86 Dec 27, 2024

Choose a reason for hiding this comment

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

This is essentially a synonym. We’ve never officially settled on what to call this fee, and here’s why:

The total fee, which we now call the executed fee, is calculated as the difference between UCP and custom prices for an order. If we assume protocol fees are not taken (set to 0), this difference is supposed to represent gas costs.

Now, let’s consider how solvers report these prices. Normally, if a solver expects to spend $10 on gas for an order, they report UCP and custom prices so that the difference reflects this cost. These values are then stored in the settlement contract.

But solvers aren’t obligated to stick to this. They could report $20 instead, meaning their custom prices are worse, making their solution less competitive with a lower score. This isn’t rational since they gain nothing from it, but it’s possible. A more realistic case is solvers reporting $5 (less than expected gas costs). This makes their custom prices better, making their solution more competitive. Solvers are incentivized to do this because even with underreported costs, they might still profit from rewards, say $50 of COW. In some cases, solvers might even report $0, which still leaves them positive and increases their competitiveness.

There’s another twist. Solvers are no longer required to report UCP prices once colocated. They can always set UCP equal to custom prices, and we’d have no way to estimate gas costs. Alternatively, solvers could bake gas costs into slightly reduced custom prices, leaving us unable to deduce what was actually spent on gas.

So, what do we call this fee? Gas fee, network fee, solver fee? It used to represent gas costs when we controlled the driver, but now it could mean anything. It might represent gas costs, half the gas costs, zero, or even negative values. In the latter case, solvers offer better custom prices than UCP 🤯 , effectively estimating zero or negative gas fees while still giving users a bit more than they gained on-chain, just to secure a juicy reward. This suggests our rewards are too high and should be lowered to create an equilibrium, incentivizing solvers to estimate custom prices properly—where "properly" means UCP minus gas costs.

Ultimately, this fee feels like a relic from the pre-colocation world. Deprecating it might be the right move, but it’s non-trivial to approximate gas costs per order in a batched settlement. For now, it persists because the frontend needs it to show users estimated costs.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Actually, thanks for bringing this up. After some thinking, I don't think my previous implementation was good enough. It definitely changes the expectation on the final executed_fee value in all of these special cases, so I reverted back to previous implementation - executed_fee and protocol_fee being completely decoupled.

Bottom line, assuming that executed_fee represents protocol_fee + gas fee and always >= than protocol_fee is just not guaranteed.

Copy link
Contributor

Choose a reason for hiding this comment

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

Oh, thanks a lot. That is much clearer now 🙌

Comment on lines +413 to +424
/// Fee per trade in a solution. These fees are taken for the execution of the
/// trade.
#[derive(Debug, Clone)]
pub struct FeeBreakdown {
/// Fee reported by solvers, representing difference between UCP and custom
/// prices.
pub executed: eth::TokenAmount,
/// Breakdown of protocol fees.
pub protocol: Vec<ExecutedProtocolFee>,
/// Surplus token in which all the fees were taken.
pub token: eth::TokenAddress,
}
Copy link
Contributor

Choose a reason for hiding this comment

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

It would be helpful to include details from your comment somewhere here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants