-
Notifications
You must be signed in to change notification settings - Fork 154
[feature] add ergonomic /quote v2 endpoint (#4058) #4088
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
base: main
Are you sure you want to change the base?
[feature] add ergonomic /quote v2 endpoint (#4058) #4088
Conversation
…andle new params and return formats
…r it alongside existing endpoints
|
All contributors have signed the CLA ✍️ ✅ |
|
I have read the CLA Document and I hereby sign the CLA |
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.
Code Review
This pull request adds a new /v2/quote endpoint to simplify order creation. The implementation reuses the existing /v1/quote logic and then applies slippage. My review found a critical vulnerability in the slippage calculation that could lead to creating incorrectly priced orders. The details are in the specific comment.
| match order.kind { | ||
| OrderKind::Sell => { | ||
| // buyAmount = buyAmount * (10000 - slippageBps) / 10000 | ||
| order.buy_amount = U256::uint_try_from( | ||
| order | ||
| .buy_amount | ||
| .widening_mul(U256::from(MAX_BPS - slippage_factor)) | ||
| / U512::from(MAX_BPS), | ||
| ) | ||
| .unwrap_or(U256::MAX); | ||
| } | ||
| OrderKind::Buy => { | ||
| // sellAmount = sellAmount * (10000 + slippageBps) / 10000 | ||
| order.sell_amount = U256::uint_try_from( | ||
| order | ||
| .sell_amount | ||
| .widening_mul(U256::from(MAX_BPS + slippage_factor)) | ||
| / U512::from(MAX_BPS), | ||
| ) | ||
| .unwrap_or(U256::MAX); | ||
| } | ||
| } |
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.
The calculation MAX_BPS - slippage_factor can underflow if slippage_bps is greater than 10000. This would cause an integer underflow in release builds, resulting in an incorrect and potentially very large buy_amount for sell orders. This is a critical issue.
Using saturating_sub prevents this underflow by clamping the result at zero, which is the correct behavior for slippage >= 100%.
match order.kind {
OrderKind::Sell => {
// buyAmount = buyAmount * (10000 - slippageBps) / 10000
order.buy_amount = U256::uint_try_from(
order
.buy_amount
.widening_mul(U256::from(MAX_BPS.saturating_sub(slippage_factor)))
/ U512::from(MAX_BPS),
)
.unwrap_or(U256::MAX);
}
OrderKind::Buy => {
// sellAmount = sellAmount * (10000 + slippageBps) / 10000
order.sell_amount = U256::uint_try_from(
order
.sell_amount
.widening_mul(U256::from(MAX_BPS + slippage_factor))
/ U512::from(MAX_BPS),
)
.unwrap_or(U256::MAX);
}
}References
- This comment focuses on a logic error (integer underflow) which is a critical issue. (link)
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.
great catch!
i should probably also add validation to reject unreasonable slippage:
if request.slippage_bps > 1000 { // 10% max slippage
return Err(OrderQuoteError::InvalidSlippage);
}
Can you elaborate on this? |
|
Hello, I would like to share this document which outlines the potential features of the v2. I want to be part of verifying the new interface proposal, and also front-end as creator of the SDK. Here is a write up about the feature https://www.notion.so/cownation/Quote-API-v2-2f18da5f04ca8032b09de7e2f409e46b?source=copy_link |
here: It does compile with just: And tests still pass. Making the fix here would be outside the scope of this PR, hence why I ignored. |
|
@theghostmac it compiles just fine in Regardless, that case should always be ok because Rust knows the size of all parties involved, LHS would be the |
|
true. i would go ahead and give @anxolin's document a read. although i do understand that some of it is internal. |
|
Hei @theghostmac, sorry there was probably a misunderstanding on my part. By reading the PR title "add ergonomic /quote v2 endpoint" I thought you were going to work on the evolution of the API we envisioned for easy integrations. The document I shared is not complete, but would give you an overview of the problem. Our SDK solves most of it, so its a great source for inspiration. The screenshots at the end try to show the 3 main prices UIs show and therefore, its handy to return in the endpoint DTO. I understand now you were not intended to solve exactly this problem and your PR had a very different scope. |
|
Hi @anxolin, Yes, I would love to work on the full spec. I paused because I thought it was internal. I can continue now. |
…amount helper func
… conversion limitation, and partner fee currency handling, then add unit tests for all methods
|
This pull request has been marked as stale because it has been inactive a while. Please update this pull request or it will be automatically closed. |
|
hi @MartinquaXD, @anxolin, anybody... is it possible to get feedback about this before it gets closed by GA bot? |
Description
This PR introduces a new
/api/v2/quoteendpoint that simplifies order creation for frontend applications by providing a complete, ready-to-sign response.The existing
/quoteendpoint returns raw price and fee data, requiring frontends to manually calculate slippage protection and apply fees. This has been a common source of confusion and errors.The new
v2endpoint solves this by returning:OrderCreationobject ready to sign (withfeeAmountset to0per the solver-competition model.beforeAllFees,afterNetworkCosts, andafterSlippageChanges
OrderQuoteRequestV2,OrderQuoteV2,QuoteBreakdown,CostBreakdowntypes incrates/model/src/quote.rs.calculate_quote_v2inQuoteHandler(crates/orderbook/src/quoter.rs) to handle slippage math and zero out fees.POST /api/v2/quoteendpoint incrates/orderbook/src/api/post_quote.rs.v2/quoteroute in the orderbook API router (crates/orderbook/src/api.rs).How to test
cargo test -p orderbook api::post_quotecargo test -p orderbook quoter::tests/api/v2/quotewith a payload containingslippageBps.Architectural notes:
calculate_quote_internal()now feedscalculate_quote()andcalculate_quote_v2()unadjusted_quote(), so i avoided double-counting by reusing that instead.buy_token_pricein price feed for the exact conversionFuture work
setPReSignatureRelated Issues
Fixes # #4058