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

refactor/test(CL): Stricter rounding behavior in CL math methods; unit tests at low price level #6369

Merged
merged 6 commits into from
Sep 14, 2023

Conversation

p0mvn
Copy link
Member

@p0mvn p0mvn commented Sep 11, 2023

Closes: #6353

What is the purpose of the change

This effort was started with the goal of testing CL math at low price level to make sure that there are no unexpected error blow-ups or rounding behavior at the extended price level.

While working on this, I noticed that there were small rounding direction issues in:
GetNextSqrtPriceFromAmount0InRoundingUp. They ended up being more apparent at the lower min spot price range. As a result, the fix is added here.

Additionally, in follow-up to #6352, I realized that dividing by a bigger sqrt price first and smaller second leads to a more accurate result in CalcAmount0Delta. This is consistent with how Uniswap does it. See here:
https://github.com/Uniswap/v3-core/blob/412d9b236a1e75a98568d49b1aeb21e3a1430544/contracts/libraries/SqrtPriceMath.sol#L159-L172

  • Note that sqrtRatioBX96 is the larger one. In our case, we were dividing by a small value first, ending up with a less precise result.

Testing and Verifying

  • Added tests + covered by existing

Documentation and Release Note

  • Does this pull request introduce a new feature or user-facing behavior changes?
  • Changelog entry added to Unreleased section of CHANGELOG.md?

Where is the change documented?

  • Specification (x/{module}/README.md)
  • Osmosis documentation site
  • Code comments?
  • N/A

Comment on lines +21 to 22
// TODO (perf): consider better conversion helpers to minimize reallocations.
amountBigDec := osmomath.BigDecFromDec(amount.ToLegacyDec())
Copy link
Member Author

Choose a reason for hiding this comment

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

Note to reviewer:

tracking these here: #6370

Copy link
Contributor

Choose a reason for hiding this comment

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

I think this pr can resolve this TODO #6409

Comment on lines +30 to 31
// TODO (perf): consider Dec() function that does not reallocate
return amountBigDec.MulMut(product).QuoMut(diff).Dec()
Copy link
Member Author

Choose a reason for hiding this comment

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

Note: this should be addressed by #6261, CC: @pysel

Comment on lines +52 to 53
// TODO (perf): consider Dec() function that does not reallocate
return amountBigDec.QuoMut(diff).Dec()
Copy link
Member Author

Choose a reason for hiding this comment

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

Note: this should be addressed by #6261, CC: @pysel

@p0mvn p0mvn marked this pull request as ready for review September 11, 2023 20:37
Copy link
Member

@czarcas7ic czarcas7ic left a comment

Choose a reason for hiding this comment

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

Approving but want to have discussion on the one comment below. I might just be missing something but it seems like we should be erroring on this case.

Comment on lines 332 to 342
"low price range (with round up)": {
liquidity: smallLiquidity,
sqrtPA: sqrtANearMin,
sqrtPB: sqrtBNearMin,
roundUp: true,
// from clmath decimal import *
// calc_amount_one_delta(liq, sqrtPriceA, sqrtPriceB, False)
// Actual result: 0.000000000000000000000000000103787163
// Gets rounded up to 1. Is this acceptable when the multiplicative difference is so large?
amount1Expected: osmomath.MustNewBigDecFromStr("0.000000000000000000000000000103787163").Ceil(),
},
Copy link
Member

@czarcas7ic czarcas7ic Sep 13, 2023

Choose a reason for hiding this comment

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

This seems problematic right? Shouldn't any calculation for amount1 being less than 1 just fail?

I think this might be okay since we only ever use round up to be in favor of the pool, but I think this would translate to larger slippage on the users end (but the slippage here is 1 unit so likely fine).

Copy link
Contributor

Choose a reason for hiding this comment

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

My recollection of digging into this during prelaunch internal reviews was that this was at least not a safety issue. We probably want to minimize rounding error in general but it's probably okay to overround to be safe.

Copy link
Member Author

Choose a reason for hiding this comment

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

This seems problematic right? Shouldn't any calculation for amount1 being less than 1 just fail?

No, there are many instances where it is valid. E.g. no liquidity, adjacent ticks with little liquidity.

On another look, I also think this is fine since done only in pool's favor (controlled by the roundUp flag)

I also reviewed Uniswap's logic and their rounding is even stricter where they take a ceiling of an intermediary calculation

Copy link
Contributor

@AlpinYukseloglu AlpinYukseloglu left a comment

Choose a reason for hiding this comment

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

LGTM, nice job. Had a clarifying question to get a better understanding of in which cases reordering is an improvement here, but regardless the implementation looks sound

x/concentrated-liquidity/math/math.go Show resolved Hide resolved
Comment on lines 332 to 342
"low price range (with round up)": {
liquidity: smallLiquidity,
sqrtPA: sqrtANearMin,
sqrtPB: sqrtBNearMin,
roundUp: true,
// from clmath decimal import *
// calc_amount_one_delta(liq, sqrtPriceA, sqrtPriceB, False)
// Actual result: 0.000000000000000000000000000103787163
// Gets rounded up to 1. Is this acceptable when the multiplicative difference is so large?
amount1Expected: osmomath.MustNewBigDecFromStr("0.000000000000000000000000000103787163").Ceil(),
},
Copy link
Contributor

Choose a reason for hiding this comment

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

My recollection of digging into this during prelaunch internal reviews was that this was at least not a safety issue. We probably want to minimize rounding error in general but it's probably okay to overround to be safe.

@p0mvn p0mvn merged commit 9362621 into main Sep 14, 2023
@p0mvn p0mvn deleted the roman/clmath-rounding-tests branch September 14, 2023 11:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

test(CL): math package tests at low price level
4 participants