Skip to content

Commit

Permalink
chore: merge dev
Browse files Browse the repository at this point in the history
  • Loading branch information
0xteddybear committed Jul 22, 2024
2 parents 56dcaa8 + dfbed26 commit 06f1120
Show file tree
Hide file tree
Showing 6 changed files with 119 additions and 183 deletions.
180 changes: 0 additions & 180 deletions test/unit/BPool.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -706,186 +706,6 @@ contract BPool_Unit_GetSpotPriceSansFee is BasePoolTest {
}
}

contract BPool_Unit_JoinswapExternAmountIn is BasePoolTest {
address tokenIn;

struct JoinswapExternAmountIn_FuzzScenario {
uint256 tokenAmountIn;
uint256 tokenInBalance;
uint256 tokenInDenorm;
uint256 totalSupply;
uint256 totalWeight;
uint256 swapFee;
}

function _setValues(JoinswapExternAmountIn_FuzzScenario memory _fuzz) internal {
tokenIn = tokens[0];

// Create mocks for tokenIn
_mockTransferFrom(tokenIn);

// Set balances
bPool.set__records(
tokenIn,
IBPool.Record({
bound: true,
index: 0, // NOTE: irrelevant for this method
denorm: _fuzz.tokenInDenorm
})
);
_mockPoolBalance(tokenIn, _fuzz.tokenInBalance);

// Set swapFee
_setSwapFee(_fuzz.swapFee);
// Set finalize
_setFinalize(true);
// Set totalSupply
_setTotalSupply(_fuzz.totalSupply);
// Set totalWeight
_setTotalWeight(_fuzz.totalWeight);
}

function _assumeHappyPath(JoinswapExternAmountIn_FuzzScenario memory _fuzz) internal pure {
// safe bound assumptions
_fuzz.tokenInDenorm = bound(_fuzz.tokenInDenorm, MIN_WEIGHT, MAX_WEIGHT);
_fuzz.swapFee = bound(_fuzz.swapFee, MIN_FEE, MAX_FEE);
_fuzz.totalWeight = bound(_fuzz.totalWeight, MIN_WEIGHT * TOKENS_AMOUNT, MAX_TOTAL_WEIGHT);

_fuzz.totalSupply = bound(_fuzz.totalSupply, INIT_POOL_SUPPLY, type(uint256).max);
_fuzz.tokenInBalance = bound(_fuzz.tokenInBalance, MIN_BALANCE, type(uint256).max);

// max
vm.assume(_fuzz.tokenInBalance < type(uint256).max - _fuzz.tokenAmountIn);

// MAX_IN_RATIO
vm.assume(_fuzz.tokenInBalance < type(uint256).max / MAX_IN_RATIO);
vm.assume(_fuzz.tokenAmountIn <= bmul(_fuzz.tokenInBalance, MAX_IN_RATIO));

// internal calculation for calcPoolOutGivenSingleIn
_assumeCalcPoolOutGivenSingleIn(
_fuzz.tokenInDenorm,
_fuzz.tokenInBalance,
_fuzz.tokenAmountIn,
_fuzz.swapFee,
_fuzz.totalWeight,
_fuzz.totalSupply
);
}

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

function test_Revert_NotFinalized(JoinswapExternAmountIn_FuzzScenario memory _fuzz) public happyPath(_fuzz) {
_setFinalize(false);

vm.expectRevert(IBPool.BPool_PoolNotFinalized.selector);
bPool.joinswapExternAmountIn(tokenIn, _fuzz.tokenAmountIn, 0);
}

function test_Revert_NotBound(
JoinswapExternAmountIn_FuzzScenario memory _fuzz,
address _tokenIn
) public happyPath(_fuzz) {
assumeNotForgeAddress(_tokenIn);

vm.expectRevert(IBPool.BPool_TokenNotBound.selector);
bPool.joinswapExternAmountIn(_tokenIn, _fuzz.tokenAmountIn, 0);
}

function test_Revert_TokenAmountInAboveMaxIn(JoinswapExternAmountIn_FuzzScenario memory _fuzz)
public
happyPath(_fuzz)
{
uint256 _tokenAmountIn = bmul(_fuzz.tokenInBalance, MAX_IN_RATIO);

vm.expectRevert(IBPool.BPool_TokenAmountInAboveMaxRatio.selector);
bPool.joinswapExternAmountIn(tokenIn, _tokenAmountIn + 1, 0);
}

function test_Revert_PoolAmountOutBelowMinPoolAmountOut(
JoinswapExternAmountIn_FuzzScenario memory _fuzz,
uint256 _minPoolAmountOut
) public happyPath(_fuzz) {
uint256 _poolAmountIn = calcPoolOutGivenSingleIn(
_fuzz.tokenInBalance,
_fuzz.tokenInDenorm,
_fuzz.totalSupply,
_fuzz.totalWeight,
_fuzz.tokenAmountIn,
_fuzz.swapFee
);
_minPoolAmountOut = bound(_minPoolAmountOut, _poolAmountIn + 1, type(uint256).max);

vm.expectRevert(IBPool.BPool_PoolAmountOutBelowMinPoolAmountOut.selector);
bPool.joinswapExternAmountIn(tokenIn, _fuzz.tokenAmountIn, _minPoolAmountOut);
}

function test_Revert_Reentrancy(JoinswapExternAmountIn_FuzzScenario memory _fuzz) public {
_expectRevertByReentrancy();
bPool.joinswapExternAmountIn(tokenIn, _fuzz.tokenAmountIn, 0);
}

function test_Emit_LogJoin(JoinswapExternAmountIn_FuzzScenario memory _fuzz) public happyPath(_fuzz) {
vm.expectEmit();
emit IBPool.LOG_JOIN(address(this), tokenIn, _fuzz.tokenAmountIn);

bPool.joinswapExternAmountIn(tokenIn, _fuzz.tokenAmountIn, 0);
}

function test_Set_ReentrancyLock(JoinswapExternAmountIn_FuzzScenario memory _fuzz) public happyPath(_fuzz) {
_expectSetReentrancyLock();
bPool.joinswapExternAmountIn(tokenIn, _fuzz.tokenAmountIn, 0);
}

function test_Mint_PoolShare(JoinswapExternAmountIn_FuzzScenario memory _fuzz) public happyPath(_fuzz) {
(uint256 _poolAmountOut) = bPool.joinswapExternAmountIn(tokenIn, _fuzz.tokenAmountIn, 0);

assertEq(bPool.totalSupply(), _fuzz.totalSupply + _poolAmountOut);
}

function test_Push_PoolShare(JoinswapExternAmountIn_FuzzScenario memory _fuzz) public happyPath(_fuzz) {
uint256 _balanceBefore = bPool.balanceOf(address(this));

(uint256 _poolAmountOut) = bPool.joinswapExternAmountIn(tokenIn, _fuzz.tokenAmountIn, 0);

assertEq(bPool.balanceOf(address(this)), _balanceBefore + _poolAmountOut);
}

function test_Pull_Underlying(JoinswapExternAmountIn_FuzzScenario memory _fuzz) public happyPath(_fuzz) {
vm.expectCall(
address(tokenIn),
abi.encodeWithSelector(IERC20.transferFrom.selector, address(this), address(bPool), _fuzz.tokenAmountIn)
);
bPool.joinswapExternAmountIn(tokenIn, _fuzz.tokenAmountIn, 0);
}

function test_Returns_PoolAmountOut(JoinswapExternAmountIn_FuzzScenario memory _fuzz) public happyPath(_fuzz) {
uint256 _expectedPoolAmountOut = calcPoolOutGivenSingleIn(
_fuzz.tokenInBalance,
_fuzz.tokenInDenorm,
_fuzz.totalSupply,
_fuzz.totalWeight,
_fuzz.tokenAmountIn,
_fuzz.swapFee
);

(uint256 _poolAmountOut) = bPool.joinswapExternAmountIn(tokenIn, _fuzz.tokenAmountIn, 0);

assertEq(_poolAmountOut, _expectedPoolAmountOut);
}

function test_Emit_LogCall(JoinswapExternAmountIn_FuzzScenario memory _fuzz) public happyPath(_fuzz) {
vm.expectEmit();
bytes memory _data = abi.encodeWithSelector(BPool.joinswapExternAmountIn.selector, tokenIn, _fuzz.tokenAmountIn, 0);
emit IBPool.LOG_CALL(BPool.joinswapExternAmountIn.selector, address(this), _data);

bPool.joinswapExternAmountIn(tokenIn, _fuzz.tokenAmountIn, 0);
}
}

contract BPool_Unit_JoinswapPoolAmountOut is BasePoolTest {
address tokenIn;

Expand Down
3 changes: 0 additions & 3 deletions test/unit/BPool/BPoolBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,6 @@ contract BPoolBase is Test, BConst, Utils {
MockBPool public bPool;
address public deployer = makeAddr('deployer');

uint256 public tokenWeight = 1e18;
uint256 public totalWeight = 10e18;

function setUp() public virtual {
vm.prank(deployer);
bPool = new MockBPool();
Expand Down
2 changes: 2 additions & 0 deletions test/unit/BPool/BPool_Bind.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import {IBPool} from 'interfaces/IBPool.sol';

contract BPoolBind is BPoolBase {
uint256 public tokenBindBalance = 100e18;
uint256 public tokenWeight = 1e18;
uint256 public totalWeight = 10e18;

function setUp() public virtual override {
super.setUp();
Expand Down
95 changes: 95 additions & 0 deletions test/unit/BPool/BPool_JoinswapExternAmountIn.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.25;

import {BPoolBase} from './BPoolBase.sol';
import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';

import {BNum} from 'contracts/BNum.sol';
import {IBPool} from 'interfaces/IBPool.sol';

contract BPoolJoinswapExternAmountIn is BPoolBase, BNum {
address public tokenIn;

// Valid scenario
uint256 public tokenAmountIn = 1e18;
uint256 public tokenInWeight = 2e18;
uint256 public totalWeight = 10e18;
uint256 public tokenInBalance = 50e18;

// (((tokenAmountIn*(1-(1-tokenInWeight/totalWeight)*MIN_FEE)+tokenInBalance)/tokenInBalance)^(tokenInWeight/totalWeight))*INIT_POOL_SUPPLY - INIT_POOL_SUPPLY
// (((1*(1-(1-2/10)*(10^-6))+50)/50)^(2/10))*100 - 100
// 0.396837555601045600
uint256 public expectedPoolOut = 0.3968375556010456e18;

function setUp() public virtual override {
super.setUp();
tokenIn = tokens[0];
bPool.set__finalized(true);
// mint an initial amount of pool shares (expected to happen at _finalize)
bPool.call__mintPoolShare(INIT_POOL_SUPPLY);
bPool.set__tokens(_tokensToMemory());
bPool.set__totalWeight(totalWeight);
bPool.set__records(tokenIn, IBPool.Record({bound: true, index: 0, denorm: tokenInWeight}));
vm.mockCall(tokenIn, abi.encodePacked(IERC20.balanceOf.selector), abi.encode(uint256(tokenInBalance)));
}

function test_RevertWhen_ReentrancyLockIsSet() external {
bPool.call__setLock(_MUTEX_TAKEN);
// it should revert
vm.expectRevert(IBPool.BPool_Reentrancy.selector);
bPool.joinswapExternAmountIn(tokenIn, tokenAmountIn, expectedPoolOut);
}

function test_RevertWhen_PoolIsNotFinalized() external {
bPool.set__finalized(false);
// it should revert
vm.expectRevert(IBPool.BPool_PoolNotFinalized.selector);
bPool.joinswapExternAmountIn(tokenIn, tokenAmountIn, expectedPoolOut);
}

function test_RevertWhen_TokenIsNotBound() external {
// it should revert
vm.expectRevert(IBPool.BPool_TokenNotBound.selector);
bPool.joinswapExternAmountIn(makeAddr('unknown token'), tokenAmountIn, expectedPoolOut);
}

function test_RevertWhen_TokenAmountInExceedsMaxRatio(uint256 amountIn) external {
amountIn = bound(amountIn, bdiv(INIT_POOL_SUPPLY, MAX_IN_RATIO) + 1, type(uint256).max);
// it should revert
vm.expectRevert(IBPool.BPool_TokenAmountInAboveMaxRatio.selector);
bPool.joinswapExternAmountIn(tokenIn, amountIn, expectedPoolOut);
}

function test_RevertWhen_CalculatedPoolAmountOutIsLessThanExpected(uint256 expectedPoolOut_) external {
expectedPoolOut_ = bound(expectedPoolOut_, expectedPoolOut + 1, type(uint256).max);
// it should revert
vm.expectRevert(IBPool.BPool_PoolAmountOutBelowMinPoolAmountOut.selector);
bPool.joinswapExternAmountIn(tokenIn, tokenAmountIn, expectedPoolOut_);
}

function test_WhenPreconditionsAreMet() external {
// it sets reentrancy lock
bPool.expectCall__setLock(_MUTEX_TAKEN);
// it queries the contracts token in balance
vm.expectCall(tokenIn, abi.encodeCall(IERC20.balanceOf, (address(bPool))));
// it calls _pullUnderlying for token
bPool.mock_call__pullUnderlying(tokenIn, address(this), tokenAmountIn);
bPool.expectCall__pullUnderlying(tokenIn, address(this), tokenAmountIn);
// it mints the pool shares
bPool.expectCall__mintPoolShare(expectedPoolOut);
// it sends pool shares to caller
bPool.expectCall__pushPoolShare(address(this), expectedPoolOut);
// it emits LOG_CALL event
bytes memory _data =
abi.encodeWithSelector(IBPool.joinswapExternAmountIn.selector, tokenIn, tokenAmountIn, expectedPoolOut);
vm.expectEmit();
emit IBPool.LOG_CALL(IBPool.joinswapExternAmountIn.selector, address(this), _data);
// it emits LOG_JOIN event for token
vm.expectEmit();
emit IBPool.LOG_JOIN(address(this), tokenIn, tokenAmountIn);
bPool.joinswapExternAmountIn(tokenIn, tokenAmountIn, expectedPoolOut);

// it clears the reentrancy lock
assertEq(_MUTEX_FREE, bPool.call__getLock());
}
}
20 changes: 20 additions & 0 deletions test/unit/BPool/BPool_JoinswapExternAmountIn.tree
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
BPool::JoinswapExternAmountIn
├── when reentrancy lock is set
│ └── it should revert
├── when pool is not finalized
│ └── it should revert
├── when token is not bound
│ └── it should revert
├── when token amount in exceeds max ratio
│ └── it should revert
├── when calculated pool amount out is less than expected
│ └── it should revert
└── when preconditions are met
├── it emits LOG_CALL event
├── it sets the reentrancy lock
├── it queries the contracts token in balance
├── it emits LOG_JOIN event for token
├── it calls _pullUnderlying for token
├── it mints the pool shares
├── it sends pool shares to caller
└── it clears the reentrancy lock
2 changes: 2 additions & 0 deletions test/unit/BPool/BPool_Unbind.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import {IBPool} from 'interfaces/IBPool.sol';

contract BPoolUnbind is BPoolBase {
uint256 public boundTokenAmount = 100e18;
uint256 public tokenWeight = 1e18;
uint256 public totalWeight = 10e18;

function setUp() public virtual override {
super.setUp();
Expand Down

0 comments on commit 06f1120

Please sign in to comment.