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

fix: rounding in the protocol's favor pt. 3 #489

Merged
merged 4 commits into from
Apr 17, 2024

Conversation

0xJabberwock
Copy link
Contributor

@0xJabberwock 0xJabberwock commented Apr 11, 2024

Description

After thoroughly reviewing the WeightedMath library in search of rounding inconsistencies, I've found some cases in which multiplications (mulUp / mulDown) or divisions (divUp / divDown) weren't favorable to the protocol.

For reference, BasePoolMath has undergone such review in #468, and StableMath in #487.

Type of change

  • Bug fix
  • New feature
  • Breaking change
  • Dependency changes
  • Code refactor / cleanup
  • Documentation or wording changes
  • Other

Checklist:

  • The diff is legible and has no extraneous changes
  • Complex code has been commented, including external interfaces
  • Tests have 100% code coverage
  • The base branch is either main, or there's a description of how to merge

Issue Resolution

Closes #488.

@0xJabberwock 0xJabberwock self-assigned this Apr 11, 2024
@joaobrunoah
Copy link
Contributor

@0xJabberwock , can you create some tests with the cases you found, to make sure the math doesn't break for these cases again?

@0xJabberwock 0xJabberwock force-pushed the fix/weighted-math-rounding branch 2 times, most recently from e3be01c to 0b0a513 Compare April 13, 2024 17:55
Copy link
Collaborator

@EndymionJkb EndymionJkb left a comment

Choose a reason for hiding this comment

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

It seems right, but it's getting late and I'm getting bleary eyed :) Similar text comments to those on StableMath.

@@ -182,6 +189,8 @@ library WeightedMath {

uint256 invariantRatioWithFees = 0;
for (uint256 i = 0; i < balances.length; ++i) {
// Round down to ultimately reduce the ratios with fees,
// which will be used later upon calculating the `nonTaxableAmount`
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
// which will be used later upon calculating the `nonTaxableAmount`
// which will be used later when calculating the `nonTaxableAmount` for each token.

Comments should always end with a period (here and elsewhere).

? bptTotalSupply.mulDown(invariantRatio - FixedPoint.ONE)
: 0;
return bptOut;
// If the invariant didn't increase for any reason, we simply don't mint BPT
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is using multiple returns more gas efficient, or do you just think it's clearer?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

What is more gas efficient is not to declare the memory variable bptOut, that is achieved by using multiple returns. Besides, it keeps consistency with the same logic found in StableMath.

@@ -212,16 +224,21 @@ library WeightedMath {

uint256 amountInWithoutFee;
{
// Round down to ultimately reduce the ratios with fees,
// which will be used later upon calculating the `nonTaxableAmount`
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
// which will be used later upon calculating the `nonTaxableAmount`
// which will be used later when calculating the `nonTaxableAmount` for each token.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In this case, the nonTaxableAmount is calculated later only for the token in.

pkg/solidity-utils/contracts/math/WeightedMath.sol Outdated Show resolved Hide resolved
pkg/solidity-utils/contracts/math/WeightedMath.sol Outdated Show resolved Hide resolved
pkg/solidity-utils/contracts/math/WeightedMath.sol Outdated Show resolved Hide resolved
uint256 amountOutWithFee;
if (invariantRatioWithoutFees > balanceRatiosWithoutFee[i]) {
uint256 nonTaxableAmount = balances[i].mulDown(invariantRatioWithoutFees.complement());
// Round accordingly to ultimately enlarge the `amountOutWithFee`, consequently reducing the
// `balanceRatio`; we prioritize incrementing the `nonTaxableAmount` over the `taxableAmount`.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
// `balanceRatio`; we prioritize incrementing the `nonTaxableAmount` over the `taxableAmount`.
// `balanceRatio`; we prioritize maximizing the `nonTaxableAmount`.

As in StableMath, would be good to explain the reasoning here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

pkg/solidity-utils/contracts/math/WeightedMath.sol Outdated Show resolved Hide resolved
pkg/solidity-utils/contracts/math/WeightedMath.sol Outdated Show resolved Hide resolved
pkg/solidity-utils/contracts/math/WeightedMath.sol Outdated Show resolved Hide resolved
Copy link
Contributor

@joaobrunoah joaobrunoah left a comment

Choose a reason for hiding this comment

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

I agree with Jeff about a fuzz testing (PR #488), or some sort of test that would fail for the code we changed, to ensure maintainability of the code.

Still, maybe it's out of scope for this PR (I opened #505 to deal with this).

@EndymionJkb
Copy link
Collaborator

I agree with Jeff about a fuzz testing (PR #488), or some sort of test that would fail for the code we changed, to ensure maintainability of the code.

Still, maybe it's out of scope for this PR (I opened #505 to deal with this).

It's probably not easy to make these tests, but I hesitate to change anything in our core math libraries without some assurance via testing that they're behaving as we expect. (In the ambiguous cases, we're betting that other "swiss cheese layers," such as adding/subtracting buffer wei to final results, will protect us when the rounding direction isn't clear.)

The tests (BasePool, Weighted, and Stable) could be done in a follow-up PR if that's easier.

@0xJabberwock
Copy link
Contributor Author

Thanks for the feedback @joaobrunoah and @EndymionJkb!

I do agree that properly testing the rounding direction would be beneficial for the health of the codebase. Therefore, I'll be tackling #505 in an upcoming PR.

@EndymionJkb
Copy link
Collaborator

I do agree that properly testing the rounding direction would be beneficial for the health of the codebase. Therefore, I'll be tackling #505 in an upcoming PR.

Sounds good. You can resolve the comment things above so it's easier to see when it's complete, but as with the others, these should be fine once updated and CI passes, pending final validation with tests, per #505.

@0xJabberwock 0xJabberwock merged commit 2079b14 into main Apr 17, 2024
10 checks passed
@0xJabberwock 0xJabberwock deleted the fix/weighted-math-rounding branch April 17, 2024 16:36
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.

Review WeightedMath rounding
3 participants