Skip to content

Commit

Permalink
test: swapExactAmountOut happyPath (#19)
Browse files Browse the repository at this point in the history
* test: add happyPath

* fix: lint

* test: suite improvement
  • Loading branch information
0xAustrian authored May 15, 2024
1 parent e24a0d3 commit 8c76c6c
Showing 1 changed file with 156 additions and 8 deletions.
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
);

// 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

0 comments on commit 8c76c6c

Please sign in to comment.