-
Notifications
You must be signed in to change notification settings - Fork 86
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
Conversation
factor: f64, | ||
max_volume_factor: f64, |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added
Co-authored-by: Martin Beckmann <[email protected]>
PriceImprovement { | ||
/// Factor of price improvement the protocol charges as a fee. | ||
/// Price improvement is the difference between executed price and the | ||
/// best quote |
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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:
- 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.
There was a problem hiding this comment.
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)
}
There was a problem hiding this comment.
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
/// Factor of price improvement the protocol charges as a fee. | ||
/// Price improvement is the difference between executed price and the | ||
/// best quote | ||
/// |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very redundant comment
There was a problem hiding this comment.
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.
let sell_amount = quote.sell_amount.max(self.order().sell.amount.0); | ||
let buy_amount = quote.buy_amount.min(self.order().buy.amount.0); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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)
896e6e8
to
a154a05
Compare
a154a05
to
dcf40f0
Compare
There was a problem hiding this 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.
afc761c
to
5f2f872
Compare
Yeah, I'd rather concentrate on the integration tests now. |
There was a problem hiding this 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( |
There was a problem hiding this comment.
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)), |
There was a problem hiding this comment.
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()
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 |
|
||
assert_eq!( | ||
sell_amount, order_sell_amount, | ||
"Sell amount should be taken from the quote for sell orders in market price." |
There was a problem hiding this comment.
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
.
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. |
Description
Closes task n3 from the #2287
How to test
TBD