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

Price improvement fee policy in driver #2375

Merged
merged 15 commits into from
Feb 12, 2024
Merged

Conversation

squadgazzz
Copy link
Contributor

@squadgazzz squadgazzz commented Feb 6, 2024

Description

Closes task n3 from the #2287

How to test

TBD

@squadgazzz squadgazzz marked this pull request as ready for review February 6, 2024 18:49
@squadgazzz squadgazzz requested a review from a team as a code owner February 6, 2024 18:49
crates/driver/openapi.yml Outdated Show resolved Hide resolved
Comment on lines 24 to 25
factor: f64,
max_volume_factor: f64,
Copy link
Contributor

Choose a reason for hiding this comment

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

Doc comments on each field would be nice.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added

crates/driver/src/domain/competition/solution/fee.rs Outdated Show resolved Hide resolved
PriceImprovement {
/// Factor of price improvement the protocol charges as a fee.
/// Price improvement is the difference between executed price and the
/// best quote
Copy link
Contributor

Choose a reason for hiding this comment

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

Price improvement is the difference between executed price and the best quote or limit price, whichever is better for the user.

/// E.g. if a user received 2000USDC for 1ETH while having a best quote
/// of 1990USDC, their surplus is 10USDC. A factor of 0.1
/// requires the solver to pay 1USDC to the protocol for
/// settling this order.
Copy link
Contributor

Choose a reason for hiding this comment

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

E.g. if a user received 2000USDC for 1ETH while having a best quote of 1995USDC and limit price of 1990USDC, their surplus is 10USDC while the price improvement is 5USDC. A factor of 0.1 requires the solver to pay 0.5USDC to the protocol for settling this order. In case the best quote was 1990USDC while the limit price was 1995USDC, the solver should also pay 0.5USDC to the protocol.

tracing::debug!(uid=?self.order().uid, fee_from_surplus=?fee_from_surplus, fee_from_volume=?fee_from_volume, protocol_fee=?(std::cmp::min(fee_from_surplus, fee_from_volume)), executed=?self.executed(), surplus_fee=?self.surplus_fee(), "calculated protocol fee");
Ok(std::cmp::min(fee_from_surplus, fee_from_volume))
let sell_amount = quote.sell_amount.max(self.order().sell.amount.0);
let buy_amount = quote.buy_amount.min(self.order().buy.amount.0);
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm super scared of these two lines, because they assume that the quote.sell_amount = order.sell_amount for sell orders and that the quote.buy_amount = order.buy_amount for buy order, which may or may not be the case.

I think the best approach is to make sure this is true is:

  1. Add the fee amount I mentioned in the previous pr Price improvemenet fee policy autopilot domain #2378 (comment) and include it in the quote exchange rate. Example:
    quote sell: 10
    quote buy : 9
    quote fee: 1 (denominated in sell)

exchange rate is 11:9
2. Scale the exchange rate (make the quote.sell_amount = order.sell_amount for sell orders and make quote.buy_amount = order.buy_amount for buy orders).

With that, I believe this might work but needs to be confirmed with a range of e2e tests.

Copy link
Contributor

@sunce86 sunce86 Feb 8, 2024

Choose a reason for hiding this comment

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

I believe this is my final proposition to fix the sell and buy amounts that are forwarded to function:

// adjust the quote sell amount to include the fee
quote.sell = quote.sell + quote.fee

// order amounts
let order_sell = self.order().sell_amount;
let order_buy = self.order().buy_amount;

// limit amounts used for surplus calculation
let (sell_amount, buy_amount) = match self.order().side {
Side:Sell => (order.sell, max(order.buy, quote.buy * (order.sell / quote.sell))),
Side:Buy => (min(order.sell, quote.sell * (order.buy / quote.buy)), order.buy)
}

Copy link
Contributor

@sunce86 sunce86 Feb 8, 2024

Choose a reason for hiding this comment

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

Here is an example to understand the scaling:

Order.sell: 20
Order.buy: 19

Quote.sell: 21
Quote: buy:18
Quote.fee: 1

Quote.sell = 21 + 1 = 22

This is now the exchange rate we need to compare the order limit amounts with.
Quote exchange rate: 22:18

Sell order:
We want to scale the quote sell amounts so that the quote.sell and order.sell are equal, so that we can safely compare their buy amounts for surplus calculation. We also need to scale the quote buy amount so that the quote exchange rate remains the same:
Quote.sell = 22 * (20 / 22) = 20 -> now it’s equal to order sell
Quote.buy = 18 * (20 / 22) = 16.364

Buy order:
We want to scale the quote buy amounts so that the quote.buy and order.buy are equal, so that we can safely compare their sell amounts for surplus calculation. We also need to scale the quote sell amount so that the quote exchange rate remains the same:
Quote.sell = 22 * (19 / 18) = 23.222
Quote.buy = 18 * (19 / 18) = 19 -> now it’s equal to order buy

crates/driver/src/domain/competition/order/fees.rs Outdated Show resolved Hide resolved
Comment on lines 24 to 27
/// Factor of price improvement the protocol charges as a fee.
/// Price improvement is the difference between executed price and the
/// best quote
///
Copy link
Contributor

Choose a reason for hiding this comment

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

Very redundant comment

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated the comment. Is it still redundant? Tried to align with other policies' comments.

Comment on lines 93 to 94
let sell_amount = quote.sell_amount.max(self.order().sell.amount.0);
let buy_amount = quote.buy_amount.min(self.order().buy.amount.0);
Copy link
Contributor

Choose a reason for hiding this comment

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

Is it guaranteed that one of the two is always equal to the order amounts? Otherwise it's possible that we take one value from the quote and another value from the order. We may have to check both ratios and commit to using either buy and sell from quote or buy and sell from order.

Copy link
Contributor

Choose a reason for hiding this comment

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

I believe this is resolved with #2375 (comment)

@squadgazzz squadgazzz force-pushed the price-improvement-policy-driver branch from 896e6e8 to a154a05 Compare February 8, 2024 13:50
@squadgazzz squadgazzz force-pushed the price-improvement-policy-driver branch from a154a05 to dcf40f0 Compare February 8, 2024 13:51
Copy link
Contributor

@sunce86 sunce86 left a comment

Choose a reason for hiding this comment

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

Nice.
With added unit tests we made sure our idea around reference amounts might work.

Do you think we can now add a test suite for the calculate_fee function? Or maybe we should test that through the driver tests instead.

@squadgazzz squadgazzz force-pushed the price-improvement-policy-driver branch from afc761c to 5f2f872 Compare February 8, 2024 17:48
@squadgazzz
Copy link
Contributor Author

Do you think we can now add a test suite for the calculate_fee function? Or maybe we should test that through the driver tests instead.

Yeah, I'd rather concentrate on the integration tests now.

Copy link
Contributor

@sunce86 sunce86 left a comment

Choose a reason for hiding this comment

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

Lg!

@@ -192,6 +230,40 @@ fn apply_factor(amount: eth::U256, factor: f64) -> Result<eth::U256, Error> {
/ 10000)
}

fn adjusted_price_improvement_amounts(
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: Some comments that sumarize what this function does and why, would be nice.
Also, fn name is long but not clear enough.

I would suggest explaining this in terms of limit_amounts, which are taken either from order or quote amounts. Also, explain why we do scaling - because in order to meaningfully compare buy amounts, for example, sell amounts for order and quote need to be equal.

maybe even providing an example or referring to exact unit test. I often get back to this example to remind myself of the logic.

maybe also renaming reference amounts to limit amounts

competition::order::fees::Quote {
sell: eth::Asset {
amount: eth::TokenAmount(self.sell_amount),
token: eth::TokenAddress(eth::ContractAddress(sell_token)),
Copy link
Contributor

Choose a reason for hiding this comment

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

nit:

amount: self.sell_amount.into()
token: sell_token.into()

@sunce86
Copy link
Contributor

sunce86 commented Feb 12, 2024

Can we merge this PR as I have a "rank by surplus" code that conflicts with some of the changes of this PR?

We have e2e tests that confirm this change doesn't break existing Surplus protocol fee. For price improvement protocol fee, test can be part of a separate PR.


assert_eq!(
sell_amount, order_sell_amount,
"Sell amount should be taken from the quote for sell orders in market price."
Copy link
Contributor

Choose a reason for hiding this comment

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

With this comment I would expect the assertion to be sell_amount == quote_sell_amount.

@squadgazzz
Copy link
Contributor Author

Can we merge this PR as I have a "rank by surplus" code that conflicts with some of the changes of this PR?

We have e2e tests that confirm this change doesn't break existing Surplus protocol fee. For price improvement protocol fee, test can be part of a separate PR.

Merging it, the remaining comments will be addressed in a separate PR.

@squadgazzz squadgazzz enabled auto-merge (squash) February 12, 2024 11:22
@squadgazzz squadgazzz merged commit 72cac8c into main Feb 12, 2024
9 checks passed
@squadgazzz squadgazzz deleted the price-improvement-policy-driver branch February 12, 2024 11:26
@github-actions github-actions bot locked and limited conversation to collaborators Feb 12, 2024
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.

4 participants