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: swapExactAmountOut happyPath #19

Merged
merged 3 commits into from
May 15, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
164 changes: 156 additions & 8 deletions test/unit/BPool.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {Utils} from 'test/unit/Utils.sol';
// TODO: remove once `private` keyword is removed in all test cases
/* solhint-disable */

abstract contract BasePoolTest is Test, BConst, Utils {
abstract contract BasePoolTest is Test, BConst, Utils, BMath {
using LibString for *;

uint256 public constant TOKENS_AMOUNT = 3;
Expand Down Expand Up @@ -81,6 +81,34 @@ abstract contract BasePoolTest is Test, BConst, Utils {
function _setTotalSupply(uint256 _totalSupply) internal {
_setPoolBalance(address(0), _totalSupply);
}

function _assumeCalcSpotPrice(
uint256 _tokenInBalance,
uint256 _tokenInDenorm,
uint256 _tokenOutBalance,
uint256 _tokenOutDenorm,
uint256 _swapFee
) internal pure {
uint256 _numer = bdiv(_tokenInBalance, _tokenInDenorm);
uint256 _denom = bdiv(_tokenOutBalance, _tokenOutDenorm);
uint256 _ratio = bdiv(_numer, _denom);
uint256 _scale = bdiv(BONE, bsub(BONE, _swapFee));
vm.assume(_ratio < type(uint256).max / _scale);
}

function _assumeCalcInGivenOut(
uint256 _tokenOutDenorm,
uint256 _tokenInDenorm,
uint256 _tokenOutBalance,
uint256 _tokenAmountOut,
uint256 _tokenInBalance
) internal pure {
uint256 _weightRatio = bdiv(_tokenOutDenorm, _tokenInDenorm);
uint256 _diff = bsub(_tokenOutBalance, _tokenAmountOut);
uint256 _y = bdiv(_tokenOutBalance, _diff);
uint256 _foo = bpow(_y, _weightRatio);
vm.assume(bsub(_foo, BONE) < type(uint256).max / _tokenInBalance);
}
}

contract BPool_Unit_Constructor is BasePoolTest {
Expand Down Expand Up @@ -505,7 +533,7 @@ contract BPool_Unit_ExitPool is BasePoolTest {
function test_Emit_LogCall() private view {}
}

contract BPool_Unit_SwapExactAmountIn is BasePoolTest, BMath {
contract BPool_Unit_SwapExactAmountIn is BasePoolTest {
address tokenIn;
address tokenOut;

Expand Down Expand Up @@ -554,7 +582,7 @@ contract BPool_Unit_SwapExactAmountIn is BasePoolTest, BMath {
_setFinalize(true);
}

function _assumeHappyPath(SwapExactAmountIn_FuzzScenario memory _fuzz) internal view {
function _assumeHappyPath(SwapExactAmountIn_FuzzScenario memory _fuzz) internal pure {
// safe bound assumptions
_fuzz.tokenInDenorm = bound(_fuzz.tokenInDenorm, MIN_WEIGHT, MAX_WEIGHT);
_fuzz.tokenOutDenorm = bound(_fuzz.tokenOutDenorm, MIN_WEIGHT, MAX_WEIGHT);
Expand All @@ -573,11 +601,9 @@ contract BPool_Unit_SwapExactAmountIn is BasePoolTest, BMath {
vm.assume(_fuzz.tokenInBalance + _fuzz.tokenAmountIn < type(uint256).max / _fuzz.tokenInDenorm);

// internal calculation for calcSpotPrice
uint256 _numer = bdiv(_fuzz.tokenInBalance, _fuzz.tokenInDenorm);
uint256 _denom = bdiv(_fuzz.tokenOutBalance, _fuzz.tokenOutDenorm);
uint256 _ratio = bdiv(_numer, _denom);
uint256 _scale = bdiv(BONE, bsub(BONE, _fuzz.swapFee));
vm.assume(_ratio < type(uint256).max / _scale);
_assumeCalcSpotPrice(
_fuzz.tokenInBalance, _fuzz.tokenInDenorm, _fuzz.tokenOutBalance, _fuzz.tokenOutDenorm, _fuzz.swapFee
);

// MAX_IN_RATIO
vm.assume(_fuzz.tokenAmountIn <= bmul(_fuzz.tokenInBalance, MAX_IN_RATIO));
Expand Down Expand Up @@ -646,6 +672,128 @@ contract BPool_Unit_SwapExactAmountIn is BasePoolTest, BMath {
}

contract BPool_Unit_SwapExactAmountOut is BasePoolTest {
address tokenIn;
address tokenOut;

struct SwapExactAmountOut_FuzzScenario {
uint256 tokenAmountOut;
uint256 tokenInBalance;
uint256 tokenInDenorm;
uint256 tokenOutBalance;
uint256 tokenOutDenorm;
uint256 swapFee;
}

function _setValues(SwapExactAmountOut_FuzzScenario memory _fuzz) internal {
tokenIn = tokens[0];
tokenOut = tokens[1];

// Create mocks for tokenIn and tokenOut (only use the first 2 tokens)
_mockTransferFrom(tokenIn);
_mockTransfer(tokenOut);

// Set balances
_setRecord(
tokenIn,
BPool.Record({
bound: true,
index: 0, // NOTE: irrelevant for this method
denorm: _fuzz.tokenInDenorm,
balance: _fuzz.tokenInBalance
})
);
_setRecord(
tokenOut,
BPool.Record({
bound: true,
index: 0, // NOTE: irrelevant for this method
denorm: _fuzz.tokenOutDenorm,
balance: _fuzz.tokenOutBalance
})
);

// Set swapFee
_setSwapFee(_fuzz.swapFee);
// Set public swap
_setPublicSwap(true);
// Set finalize
_setFinalize(true);
}

function _assumeHappyPath(SwapExactAmountOut_FuzzScenario memory _fuzz) internal view {
// safe bound assumptions
_fuzz.tokenInDenorm = bound(_fuzz.tokenInDenorm, MIN_WEIGHT, MAX_WEIGHT);
_fuzz.tokenOutDenorm = bound(_fuzz.tokenOutDenorm, MIN_WEIGHT, MAX_WEIGHT);
_fuzz.swapFee = bound(_fuzz.swapFee, MIN_FEE, MAX_FEE);

// min
vm.assume(_fuzz.tokenInBalance >= MIN_BALANCE);
vm.assume(_fuzz.tokenOutBalance >= MIN_BALANCE);

// max - calcSpotPrice (spotPriceBefore)
vm.assume(_fuzz.tokenInBalance < type(uint256).max / _fuzz.tokenInDenorm);
vm.assume(_fuzz.tokenOutBalance < type(uint256).max / _fuzz.tokenOutDenorm);

// max - calcSpotPrice (spotPriceAfter)
vm.assume(_fuzz.tokenAmountOut < type(uint256).max - _fuzz.tokenOutBalance);
vm.assume(_fuzz.tokenOutBalance + _fuzz.tokenAmountOut < type(uint256).max / _fuzz.tokenOutDenorm);

// internal calculation for calcSpotPrice (spotPriceBefore)
_assumeCalcSpotPrice(
_fuzz.tokenInBalance, _fuzz.tokenInDenorm, _fuzz.tokenOutBalance, _fuzz.tokenOutDenorm, _fuzz.swapFee
);
wei3erHase marked this conversation as resolved.
Show resolved Hide resolved

// MAX_OUT_RATIO
vm.assume(_fuzz.tokenAmountOut <= bmul(_fuzz.tokenOutBalance, MAX_OUT_RATIO));

// L364 BPool.sol
uint256 _spotPriceBefore = calcSpotPrice(
_fuzz.tokenInBalance, _fuzz.tokenInDenorm, _fuzz.tokenOutBalance, _fuzz.tokenOutDenorm, _fuzz.swapFee
);

// internal calculation for calcInGivenOut
_assumeCalcInGivenOut(
_fuzz.tokenOutDenorm, _fuzz.tokenInDenorm, _fuzz.tokenOutBalance, _fuzz.tokenAmountOut, _fuzz.tokenInBalance
);

uint256 _tokenAmountIn = calcInGivenOut(
_fuzz.tokenInBalance,
_fuzz.tokenInDenorm,
_fuzz.tokenOutBalance,
_fuzz.tokenOutDenorm,
_fuzz.tokenAmountOut,
_fuzz.swapFee
);

vm.assume(_tokenAmountIn > BONE);
vm.assume(bmul(_spotPriceBefore, _fuzz.tokenAmountOut) <= _tokenAmountIn);

// max - calcSpotPrice (spotPriceAfter)
vm.assume(_tokenAmountIn < type(uint256).max - _fuzz.tokenInBalance);
vm.assume(_fuzz.tokenInBalance + _tokenAmountIn < type(uint256).max / _fuzz.tokenInDenorm);

// internal calculation for calcSpotPrice (spotPriceAfter)
_assumeCalcSpotPrice(
_fuzz.tokenInBalance + _tokenAmountIn,
_fuzz.tokenInDenorm,
_fuzz.tokenOutBalance - _fuzz.tokenAmountOut,
_fuzz.tokenOutDenorm,
_fuzz.swapFee
);
}

modifier happyPath(SwapExactAmountOut_FuzzScenario memory _fuzz) {
_assumeHappyPath(_fuzz);
_setValues(_fuzz);
_;
}

function test_HappyPath(SwapExactAmountOut_FuzzScenario memory _fuzz) public happyPath(_fuzz) {
uint256 _maxPrice = type(uint256).max;
uint256 _maxAmountIn = type(uint256).max;
bPool.swapExactAmountOut(tokenIn, _maxAmountIn, tokenOut, _fuzz.tokenAmountOut, _maxPrice);
}

function test_Revert_NotBoundTokenIn() private view {}

function test_Revert_NotBoundTokenOut() private view {}
Expand Down
Loading