Skip to content

Commit

Permalink
Merge branch 'dev' into feat/deployment-script
Browse files Browse the repository at this point in the history
  • Loading branch information
wei3erHase authored Jul 11, 2024
2 parents c5fe03f + b795bfb commit c27b95b
Show file tree
Hide file tree
Showing 9 changed files with 205 additions and 254 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"scripts": {
"build": "forge build",
"build:optimized": "FOUNDRY_PROFILE=optimized forge build",
"coverage": "forge coverage --match-contract Unit",
"coverage": "forge coverage --match-path 'test/unit/**'",
"deploy:bcowfactory:mainnet": "bash -c 'source .env && forge script DeployBCoWFactory -vvvvv --rpc-url $MAINNET_RPC --broadcast --chain mainnet --private-key $MAINNET_DEPLOYER_PK --verify --etherscan-api-key $ETHERSCAN_API_KEY'",
"deploy:bcowfactory:testnet": "bash -c 'source .env && forge script DeployBCoWFactory -vvvvv --rpc-url $SEPOLIA_RPC --broadcast --chain sepolia --private-key $SEPOLIA_DEPLOYER_PK --verify --etherscan-api-key $ETHERSCAN_API_KEY'",
"deploy:bfactory:mainnet": "bash -c 'source .env && forge script DeployBFactory -vvvvv --rpc-url $MAINNET_RPC --broadcast --chain mainnet --private-key $MAINNET_DEPLOYER_PK --verify --etherscan-api-key $ETHERSCAN_API_KEY'",
Expand Down
193 changes: 2 additions & 191 deletions test/unit/BPool.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ abstract contract BasePoolTest is Test, BConst, Utils, BMath {

// Create fake tokens
address[] memory _tokensToAdd = _getDeterministicTokenArray(TOKENS_AMOUNT);
for (uint256 i = 0; i < tokens.length; i++) {
tokens[i] = _tokensToAdd[i];
for (uint256 i = 0; i < _tokensToAdd.length; i++) {
tokens.push(_tokensToAdd[i]);
}
}

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

contract BPool_Unit_ExitPool is BasePoolTest {
struct ExitPool_FuzzScenario {
uint256 poolAmountIn;
uint256 initPoolSupply;
uint256[TOKENS_AMOUNT] balance;
}

function _setValues(ExitPool_FuzzScenario memory _fuzz) internal {
// Create mocks
for (uint256 i = 0; i < tokens.length; i++) {
_mockTransfer(tokens[i]);
}

// Set tokens
_setTokens(_tokensToMemory());

// Set balances
for (uint256 i = 0; i < tokens.length; i++) {
_setRecord(
tokens[i],
IBPool.Record({
bound: true,
index: 0, // NOTE: irrelevant for this method
denorm: 0 // NOTE: irrelevant for this method
})
);
_mockPoolBalance(tokens[i], _fuzz.balance[i]);
}

// Set LP token balance
_setPoolBalance(address(this), _fuzz.poolAmountIn); // give LP tokens to fn caller
// Set totalSupply
_setTotalSupply(_fuzz.initPoolSupply - _fuzz.poolAmountIn);
// Set finalize
_setFinalize(true);
}

function _assumeHappyPath(ExitPool_FuzzScenario memory _fuzz) internal pure {
uint256 _maxInitSupply = type(uint256).max / BONE;
_fuzz.initPoolSupply = bound(_fuzz.initPoolSupply, INIT_POOL_SUPPLY, _maxInitSupply);

uint256 _poolAmountInAfterFee = _fuzz.poolAmountIn - (_fuzz.poolAmountIn * EXIT_FEE);
vm.assume(_poolAmountInAfterFee <= _fuzz.initPoolSupply);
vm.assume(_poolAmountInAfterFee * BONE > _fuzz.initPoolSupply);
vm.assume(_poolAmountInAfterFee * BONE < type(uint256).max - (_fuzz.initPoolSupply / 2));

uint256 _ratio = bdiv(_poolAmountInAfterFee, _fuzz.initPoolSupply);
uint256 _maxBalance = type(uint256).max / (_ratio * BONE);

for (uint256 i = 0; i < _fuzz.balance.length; i++) {
_fuzz.balance[i] = bound(_fuzz.balance[i], BONE, _maxBalance);
}
}

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

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

vm.expectRevert(IBPool.BPool_PoolNotFinalized.selector);
bPool.exitPool(_fuzz.poolAmountIn, _zeroArray(tokens.length));
}

function test_Revert_InvalidPoolRatio(
ExitPool_FuzzScenario memory _fuzz,
uint256 _poolAmountIn
) public happyPath(_fuzz) {
_poolAmountIn = bound(_poolAmountIn, 0, (INIT_POOL_SUPPLY / 2 / BONE) - 1); // bdiv rounds up

vm.expectRevert(IBPool.BPool_InvalidPoolRatio.selector);
bPool.exitPool(_poolAmountIn, _zeroArray(tokens.length));
}

function test_Pull_PoolShare(ExitPool_FuzzScenario memory _fuzz) public happyPath(_fuzz) {
assertEq(bPool.balanceOf(address(this)), _fuzz.poolAmountIn);

bPool.exitPool(_fuzz.poolAmountIn, _zeroArray(tokens.length));

assertEq(bPool.balanceOf(address(this)), 0);
}

function test_Push_PoolShare(ExitPool_FuzzScenario memory _fuzz) public happyPath(_fuzz) {
address _factoryAddress = bPool.FACTORY();
uint256 _exitFee = bmul(_fuzz.poolAmountIn, EXIT_FEE);
uint256 _balanceBefore = bPool.balanceOf(_factoryAddress);

bPool.exitPool(_fuzz.poolAmountIn, _zeroArray(tokens.length));

assertEq(bPool.balanceOf(_factoryAddress), _balanceBefore - _fuzz.poolAmountIn + _exitFee);
}

function test_Burn_PoolShare(ExitPool_FuzzScenario memory _fuzz) public happyPath(_fuzz) {
uint256 _exitFee = bmul(_fuzz.poolAmountIn, EXIT_FEE);
uint256 _pAiAfterExitFee = bsub(_fuzz.poolAmountIn, _exitFee);
uint256 _totalSupplyBefore = bPool.totalSupply();

bPool.exitPool(_fuzz.poolAmountIn, _zeroArray(tokens.length));

assertEq(bPool.totalSupply(), _totalSupplyBefore - _pAiAfterExitFee);
}

function test_Set_ReentrancyLock(ExitPool_FuzzScenario memory _fuzz) public happyPath(_fuzz) {
_expectSetReentrancyLock();
bPool.exitPool(_fuzz.poolAmountIn, _zeroArray(tokens.length));
}

function test_Revert_InvalidTokenAmountOut(
ExitPool_FuzzScenario memory _fuzz,
uint256 _tokenIndex
) public happyPath(_fuzz) {
_assumeHappyPath(_fuzz);
_tokenIndex = bound(_tokenIndex, 0, TOKENS_AMOUNT - 1);
_fuzz.balance[_tokenIndex] = 0;
_setValues(_fuzz);

vm.expectRevert(IBPool.BPool_InvalidTokenAmountOut.selector);
bPool.exitPool(_fuzz.poolAmountIn, _zeroArray(tokens.length));
}

function test_Revert_TokenAmountOutBelowMinAmountOut(
ExitPool_FuzzScenario memory _fuzz,
uint256 _tokenIndex,
uint256[TOKENS_AMOUNT] memory _minAmountsOut
) public happyPath(_fuzz) {
_tokenIndex = bound(_tokenIndex, 0, TOKENS_AMOUNT - 1);

uint256 _poolTotal = _fuzz.initPoolSupply;
uint256 _exitFee = bmul(_fuzz.poolAmountIn, EXIT_FEE);
uint256 _pAiAfterExitFee = bsub(_fuzz.poolAmountIn, _exitFee);
uint256 _ratio = bdiv(_pAiAfterExitFee, _poolTotal);

for (uint256 i = 0; i < tokens.length; i++) {
uint256 _bal = _fuzz.balance[i];
uint256 _tokenAmountOut = bmul(_ratio, _bal);

_minAmountsOut[i] = _tokenIndex == i ? _tokenAmountOut + 1 : _tokenAmountOut;
}

vm.expectRevert(IBPool.BPool_TokenAmountOutBelowMinAmountOut.selector);
bPool.exitPool(_fuzz.poolAmountIn, _staticToDynamicUintArray(_minAmountsOut));
}

function test_Revert_Reentrancy(ExitPool_FuzzScenario memory _fuzz) public happyPath(_fuzz) {
_expectRevertByReentrancy();
bPool.exitPool(_fuzz.poolAmountIn, _zeroArray(tokens.length));
}

function test_Emit_TokenArrayLogExit(ExitPool_FuzzScenario memory _fuzz) public happyPath(_fuzz) {
uint256 _exitFee = bmul(_fuzz.poolAmountIn, EXIT_FEE);
uint256 _pAiAfterExitFee = bsub(_fuzz.poolAmountIn, _exitFee);
uint256 _ratio = bdiv(_pAiAfterExitFee, _fuzz.initPoolSupply);

for (uint256 i = 0; i < tokens.length; i++) {
uint256 _bal = _fuzz.balance[i];
uint256 _tokenAmountOut = bmul(_ratio, _bal);
vm.expectEmit();
emit IBPool.LOG_EXIT(address(this), tokens[i], _tokenAmountOut);
}
bPool.exitPool(_fuzz.poolAmountIn, _zeroArray(tokens.length));
}

function test_Push_TokenArrayTokenAmountOut(ExitPool_FuzzScenario memory _fuzz) public happyPath(_fuzz) {
uint256 _exitFee = bmul(_fuzz.poolAmountIn, EXIT_FEE);
uint256 _pAiAfterExitFee = bsub(_fuzz.poolAmountIn, _exitFee);
uint256 _ratio = bdiv(_pAiAfterExitFee, _fuzz.initPoolSupply);

for (uint256 i = 0; i < tokens.length; i++) {
uint256 _bal = _fuzz.balance[i];
uint256 _tokenAmountOut = bmul(_ratio, _bal);
vm.expectCall(
address(tokens[i]), abi.encodeWithSelector(IERC20.transfer.selector, address(this), _tokenAmountOut)
);
}
bPool.exitPool(_fuzz.poolAmountIn, _zeroArray(tokens.length));
}

function test_Emit_LogCall(ExitPool_FuzzScenario memory _fuzz) public happyPath(_fuzz) {
vm.expectEmit();
bytes memory _data = abi.encodeWithSelector(BPool.exitPool.selector, _fuzz.poolAmountIn, _zeroArray(tokens.length));
emit IBPool.LOG_CALL(BPool.exitPool.selector, address(this), _data);

bPool.exitPool(_fuzz.poolAmountIn, _zeroArray(tokens.length));
}
}

contract BPool_Unit_SwapExactAmountIn is SwapExactAmountInUtils {
function test_Revert_NotBoundTokenIn(
SwapExactAmountIn_FuzzScenario memory _fuzz,
Expand Down
8 changes: 2 additions & 6 deletions test/unit/BPool/BPoolBase.sol
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.25;

import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import {BConst} from 'contracts/BConst.sol';
import {Test} from 'forge-std/Test.sol';
import {IBPool} from 'interfaces/IBPool.sol';
Expand All @@ -12,17 +11,14 @@ contract BPoolBase is Test, BConst, Utils {
MockBPool public bPool;
address public deployer = makeAddr('deployer');

address public token = makeAddr('token');
uint256 public tokenWeight = 1e18;
uint256 public totalWeight = 10e18;
address public secondToken = makeAddr('secondToken');

function setUp() public virtual {
vm.prank(deployer);
bPool = new MockBPool();

vm.mockCall(token, abi.encodePacked(IERC20.transferFrom.selector), abi.encode());
vm.mockCall(token, abi.encodePacked(IERC20.transfer.selector), abi.encode());
tokens.push(makeAddr('token0'));
tokens.push(makeAddr('token1'));
}

function _setRandomTokens(uint256 _length) internal returns (address[] memory _tokensToAdd) {
Expand Down
42 changes: 25 additions & 17 deletions test/unit/BPool/BPool_Bind.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,32 @@
pragma solidity 0.8.25;

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

contract BPoolBind is BPoolBase {
uint256 public tokenBindBalance = 100e18;

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

vm.mockCall(tokens[0], abi.encodePacked(IERC20.transferFrom.selector), abi.encode());
vm.mockCall(tokens[0], abi.encodePacked(IERC20.transfer.selector), abi.encode());
}

function test_RevertWhen_ReentrancyLockIsSet() external {
bPool.call__setLock(_MUTEX_TAKEN);
vm.expectRevert(IBPool.BPool_Reentrancy.selector);
// it should revert
bPool.bind(token, tokenBindBalance, tokenWeight);
bPool.bind(tokens[0], tokenBindBalance, tokenWeight);
}

function test_RevertWhen_CallerIsNOTController(address _caller) external {
// it should revert
vm.assume(_caller != deployer);
vm.prank(_caller);
vm.expectRevert(IBPool.BPool_CallerIsNotController.selector);
bPool.bind(token, tokenBindBalance, tokenWeight);
bPool.bind(tokens[0], tokenBindBalance, tokenWeight);
}

modifier whenCallerIsController() {
Expand All @@ -28,49 +36,49 @@ contract BPoolBind is BPoolBase {
}

function test_RevertWhen_TokenIsAlreadyBound() external whenCallerIsController {
_setRecord(token, IBPool.Record({bound: true, index: 0, denorm: tokenWeight}));
_setRecord(tokens[0], IBPool.Record({bound: true, index: 0, denorm: tokenWeight}));
// it should revert
vm.expectRevert(IBPool.BPool_TokenAlreadyBound.selector);
bPool.bind(token, tokenBindBalance, tokenWeight);
bPool.bind(tokens[0], tokenBindBalance, tokenWeight);
}

function test_RevertWhen_PoolIsFinalized() external whenCallerIsController {
bPool.set__finalized(true);
// it should revert
vm.expectRevert(IBPool.BPool_PoolIsFinalized.selector);
bPool.bind(token, tokenBindBalance, tokenWeight);
bPool.bind(tokens[0], tokenBindBalance, tokenWeight);
}

function test_RevertWhen_MAX_BOUND_TOKENSTokensAreAlreadyBound() external whenCallerIsController {
_setRandomTokens(MAX_BOUND_TOKENS);
// it should revert
vm.expectRevert(IBPool.BPool_TokensAboveMaximum.selector);
bPool.bind(token, tokenBindBalance, tokenWeight);
bPool.bind(tokens[0], tokenBindBalance, tokenWeight);
}

function test_RevertWhen_TokenWeightIsTooLow() external whenCallerIsController {
// it should revert
vm.expectRevert(IBPool.BPool_WeightBelowMinimum.selector);
bPool.bind(token, tokenBindBalance, MIN_WEIGHT - 1);
bPool.bind(tokens[0], tokenBindBalance, MIN_WEIGHT - 1);
}

function test_RevertWhen_TokenWeightIsTooHigh() external whenCallerIsController {
// it should revert
vm.expectRevert(IBPool.BPool_WeightAboveMaximum.selector);
bPool.bind(token, tokenBindBalance, MAX_WEIGHT + 1);
bPool.bind(tokens[0], tokenBindBalance, MAX_WEIGHT + 1);
}

function test_RevertWhen_TooLittleBalanceIsProvided() external whenCallerIsController {
// it should revert
vm.expectRevert(IBPool.BPool_BalanceBelowMinimum.selector);
bPool.bind(token, MIN_BALANCE - 1, tokenWeight);
bPool.bind(tokens[0], MIN_BALANCE - 1, tokenWeight);
}

function test_RevertWhen_WeightSumExceedsMAX_TOTAL_WEIGHT() external whenCallerIsController {
bPool.set__totalWeight(2 * MAX_TOTAL_WEIGHT / 3);
// it should revert
vm.expectRevert(IBPool.BPool_TotalWeightAboveMaximum.selector);
bPool.bind(token, tokenBindBalance, MAX_TOTAL_WEIGHT / 2);
bPool.bind(tokens[0], tokenBindBalance, MAX_TOTAL_WEIGHT / 2);
}

function test_WhenTokenCanBeBound(uint256 _existingTokens) external whenCallerIsController {
Expand All @@ -79,24 +87,24 @@ contract BPoolBind is BPoolBase {

bPool.set__totalWeight(totalWeight);
// it calls _pullUnderlying
bPool.expectCall__pullUnderlying(token, deployer, tokenBindBalance);
bPool.expectCall__pullUnderlying(tokens[0], deployer, tokenBindBalance);
// it sets the reentrancy lock
bPool.expectCall__setLock(_MUTEX_TAKEN);
// it emits LOG_CALL event
vm.expectEmit();
bytes memory _data = abi.encodeWithSelector(IBPool.bind.selector, token, tokenBindBalance, tokenWeight);
bytes memory _data = abi.encodeWithSelector(IBPool.bind.selector, tokens[0], tokenBindBalance, tokenWeight);
emit IBPool.LOG_CALL(IBPool.bind.selector, deployer, _data);

bPool.bind(token, tokenBindBalance, tokenWeight);
bPool.bind(tokens[0], tokenBindBalance, tokenWeight);

// it clears the reentrancy lock
assertEq(bPool.call__getLock(), _MUTEX_FREE);
// it adds token to the tokens array
assertEq(bPool.call__tokens()[_existingTokens], token);
assertEq(bPool.call__tokens()[_existingTokens], tokens[0]);
// it sets the token record
assertEq(bPool.call__records(token).bound, true);
assertEq(bPool.call__records(token).denorm, tokenWeight);
assertEq(bPool.call__records(token).index, _existingTokens);
assertEq(bPool.call__records(tokens[0]).bound, true);
assertEq(bPool.call__records(tokens[0]).denorm, tokenWeight);
assertEq(bPool.call__records(tokens[0]).index, _existingTokens);
// it sets total weight
assertEq(bPool.call__totalWeight(), totalWeight + tokenWeight);
}
Expand Down
Loading

0 comments on commit c27b95b

Please sign in to comment.