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

test: btt tests for swap exact amount in #149

Merged
merged 7 commits into from
Jul 12, 2024

Conversation

0xteddybear
Copy link

@0xteddybear 0xteddybear commented Jul 9, 2024

the user not having enough tokenIn would revert on _pullUnderlying, perhaps it's better test modularization to not test that flow here, but with _pullUnderlying? tree has less branches this way, but it's less descriptive of domain cases and isn't consistent with what I just did on exitPool

do you think it's worthwhile to test the cases where

  • tokenIn.balanceOf(pool) == 0?
  • tokenOut.balanceOf(pool) == 0? combined with tokenAmountIn == 0 it could cause a division by zero in calcOutGivenIn

closes BAL-142

@0xteddybear 0xteddybear marked this pull request as ready for review July 9, 2024 20:37
Copy link

linear bot commented Jul 9, 2024

BAL-142

@0xteddybear 0xteddybear requested a review from wei3erHase July 9, 2024 20:39
│ └── it should revert
├── when returned tokenOut is less than minAmountOut
│ └── it should revert
└── when preconditions are met
Copy link
Author

Choose a reason for hiding this comment

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

ooops, realized I'm not triggering BPool_SpotPriceBeforeAboveTokenRatio. Looking into it

Copy link
Author

Choose a reason for hiding this comment

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

the reason behind that check and the MAX_{IN,OUT}_RATIO ones sure looks like could be fun properties 👀

Copy link
Member

@wei3erHase wei3erHase Jul 9, 2024

Choose a reason for hiding this comment

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

i think the rationale is avoiding fat fingers, meaning buying more than 50% of the available tokens, or selling more than 50% of the pool balance

Copy link
Author

Choose a reason for hiding this comment

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

I tried to find parameters to trigger this particular error and wasn't able to: https://www.desmos.com/calculator/onhfxbfljk

@drgorillamd you think it would be possible to formally prove this is unreachable?

I did notice that there is one testcase for swapExactAmountOut, will look into it when testing that method

Copy link
Member

Choose a reason for hiding this comment

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

to be clear, we're not planning on removing the check even if we find it unreachable, but we can then skip the test, or add a comment in the test explaining the current behaviour

Copy link
Member

Choose a reason for hiding this comment

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

I tried to find parameters to trigger this particular error and wasn't able to: https://www.desmos.com/calculator/onhfxbfljk

@drgorillamd you think it would be possible to formally prove this is unreachable?

I did notice that there is one testcase for swapExactAmountOut, will look into it when testing that method

Quickly tried with Halmos but there are some loops hiding in bnum, so I'll try with Kontrol instead (Echidna seemds to find some cases tho, for small token amount in esp, will double-check tomorrow along Kontrol tho)

Comment on lines 12 to 15
├── when spot price before swap exceeds maxPrice
│ └── it should revert
├── when spot price after swap exceeds maxPrice
│ └── it should revert
Copy link
Member

Choose a reason for hiding this comment

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

isn't there an intermediary step? if spot price before exceeds spot price after? 🤔

i do think this error is unreachable, but perhaps with smock we can have a chance of getting to it

Copy link
Member

Choose a reason for hiding this comment

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

i think we should make the spot price check after the returned tokenOut (to keep consistency with order of reverts)

@wei3erHase
Copy link
Member

wei3erHase commented Jul 9, 2024

mhhh for the zero variable hence division by zero, if they happen in the tested method i'd include them, if they happen in an underlying one i'd leave them for the specific test

│ └── it should revert
├── when token out is not bound
│ └── it should revert
├── when token in exceeds max allowed ratio
Copy link
Member

Choose a reason for hiding this comment

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

*token amount in

│ └── it should revert
├── when spot price after swap exceeds maxPrice
│ └── it should revert
├── when returned tokenOut is less than minAmountOut
Copy link
Member

@wei3erHase wei3erHase Jul 10, 2024

Choose a reason for hiding this comment

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

when calculated token amount out is less than min

Comment on lines +16 to +19
├── when spot price after swap exceeds maxPrice
│ └── it should revert
├── when spot price before swap exceeds token ratio after swap
│ └── it should revert
Copy link
Member

Choose a reason for hiding this comment

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

missing BPool_SpotPriceAfterBelowSpotPriceBefore revert (also unreachable but perhaps mockable)

Copy link
Author

Choose a reason for hiding this comment

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

mathematically it seems unreachable, and in order to mock it I should:

  • make calcSpotPrice virtual
  • manually override it in the mocking for BPool to force an external call
  • insert a mock for the call with the second set of parameters to return a value lower than for the first one. Thankfully the parameters are different in each invocation, so a regular mockCall can take care of it and we wouldn't have to make a custom override counting calls.

Adding a skipped test to be explicit about it

Copy link
Member

@wei3erHase wei3erHase left a comment

Choose a reason for hiding this comment

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

looking good, let's see how it goes after merging #148

// overriding `calcSpotPrice` in a mock:
// P_{sb} = \frac{\frac{b_i}{w_i}}{\frac{b_o}{w_o}}
// P_{sa} = \frac{\frac{b_i + a_i}{w_i}}{\frac{b_o - a_o}{w_o}}
// ...and both a_i (amount in) and a_o (amount out) are uints
Copy link
Author

Choose a reason for hiding this comment

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

@drgorillamd another proving candidate?

@@ -818,230 +818,6 @@ contract BPool_Unit_GetSpotPriceSansFee is BasePoolTest {
}
}

contract BPool_Unit_SwapExactAmountIn is SwapExactAmountInUtils {
Copy link
Author

Choose a reason for hiding this comment

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

btw I plan on taking care of the BCoWPool.verify tests, which rely on SwapExactAmountInUtils next, so we can get rid of the abstract contract as well

Copy link
Member

Choose a reason for hiding this comment

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

actually i saw today that many of the Utils.sol methods are unused, so we should make a cleaning after getting the final coverage

Copy link
Author

Choose a reason for hiding this comment

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

making a list in BAL-155

Comment on lines 106 to 109
function test_RevertWhen_SpotPriceBeforeSwapExceedsTokenRatioAfterSwap() external {
// it should revert
vm.expectRevert(IBPool.BPool_TokenAmountOutBelowMinOut.selector);
bPool.swapExactAmountIn(tokenIn, tokenAmountIn, tokenOut, expectedAmountOut + 1, spotPriceAfterSwap);
vm.skip(true);
}
Copy link
Member

Choose a reason for hiding this comment

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

this one appears to be reachable in current tests

@@ -818,195 +818,6 @@ contract BPool_Unit_GetSpotPriceSansFee is BasePoolTest {
}
}

contract BPool_Unit_ExitPool is BasePoolTest {
Copy link
Member

Choose a reason for hiding this comment

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

i think this is not the test you should be erasing in this PR

Copy link
Author

@0xteddybear 0xteddybear Jul 11, 2024

Choose a reason for hiding this comment

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

~~ 😨 ~~ hold on, I'm not seeing this in the diff 👀

Copy link
Member

Choose a reason for hiding this comment

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

ahh perhaps was on the "merging dev" commit

Comment on lines 107 to 108
// it should revert
vm.skip(true);
Copy link
Member

@wei3erHase wei3erHase Jul 11, 2024

Choose a reason for hiding this comment

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

have a set of values that triggers this :)

Suggested change
// it should revert
vm.skip(true);
uint256 tokenAmountIn_ = 30e18;
uint256 balanceTokenIn_ = 36830000000000000000000000000000;
uint256 weightTokenIn_ = 1e18;
uint256 balanceTokenOut_ = 18100000000000000000000000000000;
uint256 weightTokenOut_ = 1e18;
uint256 swapFee_ = 0.019e18;
vm.mockCall(tokenIn, abi.encodePacked(IERC20.balanceOf.selector), abi.encode(uint256(balanceTokenIn_)));
vm.mockCall(tokenOut, abi.encodePacked(IERC20.balanceOf.selector), abi.encode(uint256(balanceTokenOut_)));
bPool.set__records(tokenIn, IBPool.Record({bound: true, index: 0, denorm: weightTokenIn_}));
bPool.set__records(tokenOut, IBPool.Record({bound: true, index: 1, denorm: weightTokenOut_}));
bPool.set__swapFee(swapFee_);
// it should revert
vm.expectRevert(IBPool.BPool_SpotPriceBeforeAboveTokenRatio.selector);
bPool.swapExactAmountIn(tokenIn, tokenAmountIn_, tokenOut, 0, type(uint256).max);

Copy link
Author

Choose a reason for hiding this comment

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

how did you generate this? a fuzz run?

Copy link
Member

Choose a reason for hiding this comment

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

i used the fuzzer of legacy swapExactAmountIn tests to generate a counterproof, to hit the specific revert a combination of testFail and vm.expectRevert can be used

Copy link
Member

@wei3erHase wei3erHase left a comment

Choose a reason for hiding this comment

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

rocking it pal! 👏

@wei3erHase wei3erHase merged commit 06efb54 into dev Jul 12, 2024
4 checks passed
@wei3erHase wei3erHase deleted the test/btt-bpool-swapExactAmountIn branch July 12, 2024 07:39
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.

3 participants