From f1d6d9b29b0ae27efec66665322a261986eff5fb Mon Sep 17 00:00:00 2001 From: 0xAustrian Date: Fri, 3 May 2024 16:28:28 -0300 Subject: [PATCH 01/18] test: wip initial test structure --- test/unit/BPool.t.sol | 194 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 194 insertions(+) create mode 100644 test/unit/BPool.t.sol diff --git a/test/unit/BPool.t.sol b/test/unit/BPool.t.sol new file mode 100644 index 00000000..0167b687 --- /dev/null +++ b/test/unit/BPool.t.sol @@ -0,0 +1,194 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.23; + +import {Test} from 'forge-std/Test.sol'; + +abstract contract Base is Test { + function setUp() public {} +} + +contract BPool_Unit_Constructor is Base { + function test_Deploy() public view {} +} + +contract BPool_Unit_IsPublicSwap is Base { + function test_Returns_IsPublicSwap() public view {} +} + +contract BPool_Unit_IsFinalized is Base { + function test_Returns_IsFinalized() public view {} +} + +contract BPool_Unit_IsBound is Base { + function test_Returns_IsBound() public view {} + + function test_Returns_IsNotBound() public view {} +} + +contract BPool_Unit_GetNumTokens is Base { + function test_Returns_NumTokens() public view {} +} + +contract BPool_Unit_GetCurrentTokens is Base { + function test_Returns_CurrentTokens() public view {} + + function test_Revert_Reentrancy() public view {} +} + +contract BPool_Unit_GetFinalTokens is Base { + function test_Returns_FinalTokens() public view {} + + function test_Revert_Reentrancy() public view {} + + function test_Revert_NotFinalized() public view {} +} + +contract BPool_Unit_GetDenormalizedWeight is Base { + function test_Returns_DenormalizedWeight() public view {} + + function test_Revert_Reentrancy() public view {} + + function test_Revert_NotBound() public view {} +} + +contract BPool_Unit_GetTotalDenormalizedWeight is Base { + function test_Returns_TotalDenormalizedWeight() public view {} + + function test_Revert_Reentrancy() public view {} +} + +contract BPool_Unit_GetNormalizedWeight is Base { + function test_Returns_NormalizedWeight() public view {} + + function test_Revert_Reentrancy() public view {} + + function test_Revert_NotBound() public view {} +} + +contract BPool_Unit_GetBalance is Base { + function test_Returns_Balance() public view {} + + function test_Revert_Reentrancy() public view {} + + function test_Revert_NotBound() public view {} +} + +contract BPool_Unit_GetSwapFee is Base { + function test_Returns_SwapFee() public view {} + + function test_Revert_Reentrancy() public view {} +} + +contract BPool_Unit_GetController is Base { + function test_Returns_Controller() public view {} + + function test_Revert_Reentrancy() public view {} +} + +contract BPool_Unit_SetSwapFee is Base { + function test_Revert_Finalized() public view {} + + function test_Revert_NotController() public view {} + + function test_Revert_MinFee() public view {} + + function test_Revert_MaxFee() public view {} + + function test_Revert_Reentrancy() public view {} + + function test_Set_SwapFee() public view {} + + function test_Emit_LogCall() public view {} +} + +contract BPool_Unit_SetController is Base { + function test_Revert_NotController() public view {} + + function test_Revert_Reentrancy() public view {} + + function test_Set_Controller() public view {} + + function test_Emit_LogCall() public view {} +} + +contract BPool_Unit_SetPublicSwap is Base { + function test_Revert_Finalized() public view {} + + function test_Revert_NotController() public view {} + + function test_Revert_Reentrancy() public view {} + + function test_Set_PublicSwap() public view {} + + function test_Emit_LogCall() public view {} +} + +contract BPool_Unit_Finalize is Base { + function test_Revert_NotController() public view {} + + function test_Revert_Finalized() public view {} + + function test_Revert_MinTokens() public view {} + + function test_Revert_Reentrancy() public view {} + + function test_Set_Finalize() public view {} + + function test_Set_PublicSwap() public view {} + + function test_Mint_InitPoolSupply() public view {} + + function test_Push_InitPoolSupply() public view {} + + function test_Emit_LogCall() public view {} +} + +contract BPool_Unit_Bind is Base { + function test_Revert_NotController() public view {} + + function test_Revert_IsBound() public view {} + + function test_Revert_Finalized() public view {} + + function test_Revert_MaxPoolTokens() public view {} + + function test_Set_Record() public view {} + + function test_Set_TokenArray() public view {} + + function test_Emit_LogCall() public view {} + + function test_Call_Rebind() public view {} +} + +contract BPool_Unit_Rebind is Base { + function test_Revert_NotController() public view {} + + function test_Revert_NotBound() public view {} + + function test_Revert_Finalized() public view {} + + function test_Revert_MinWeight() public view {} + + function test_Revert_MaxWeight() public view {} + + function test_Revert_MinBalance() public view {} + + function test_Set_TotalWeightIfDenormMoreThanOldWeight() public view {} + + function test_Set_TotalWeightIfDenormLessThanOldWeight() public view {} + + function test_Revert_MaxTotalWeight() public view {} + + function test_Set_Denorm() public view {} + + function test_Set_Balance() public view {} + + function test_Pull_IfBalanceMoreThanOldBalance() public view {} + + function test_Push_UnderlyingIfBalanceLessThanOldBalance() public view {} + + function test_Push_FeeIfBalanceLessThanOldBalance() public view {} + + function test_Emit_LogCall() public view {} +} From 20f064b432a2911b7dcae71a0d78c919c3fa2c8f Mon Sep 17 00:00:00 2001 From: 0xAustrian Date: Mon, 6 May 2024 10:09:58 -0300 Subject: [PATCH 02/18] test: complete scaffolding --- test/unit/BPool.t.sol | 292 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 292 insertions(+) diff --git a/test/unit/BPool.t.sol b/test/unit/BPool.t.sol index 0167b687..1c9ecb06 100644 --- a/test/unit/BPool.t.sol +++ b/test/unit/BPool.t.sol @@ -174,6 +174,8 @@ contract BPool_Unit_Rebind is Base { function test_Revert_MinBalance() public view {} + function test_Revert_Reentrancy() public view {} + function test_Set_TotalWeightIfDenormMoreThanOldWeight() public view {} function test_Set_TotalWeightIfDenormLessThanOldWeight() public view {} @@ -192,3 +194,293 @@ contract BPool_Unit_Rebind is Base { function test_Emit_LogCall() public view {} } + +contract BPool_Unit_Unbind is Base { + function test_Revert_NotController() public view {} + + function test_Revert_NotBound() public view {} + + function test_Revert_Finalized() public view {} + + function test_Revert_Reentrancy() public view {} + + function test_Set_TotalWeight() public view {} + + function test_Set_TokenArray() public view {} + + function test_Set_Index() public view {} + + function test_Unset_TokenArray() public view {} + + function test_Unset_Record() public view {} + + function test_Push_UnderlyingBalance() public view {} + + function test_Push_UnderlyingFee() public view {} + + function test_Emit_LogCall() public view {} +} + +contract BPool_Unit_Gulp is Base { + function test_Revert_NotBound() public view {} + + function test_Revert_Reentrancy() public view {} + + function test_Set_Balance() public view {} + + function test_Emit_LogCall() public view {} +} + +contract BPool_Unit_GetSpotPrice is Base { + function test_Revert_NotBoundTokenIn() public view {} + + function test_Revert_NotBoundTokenOut() public view {} + + function test_Returns_SpotPrice() public view {} + + function test_Revert_Reentrancy() public view {} +} + +contract BPool_Unit_GetSpotPriceSansFee is Base { + function test_Revert_NotBoundTokenIn() public view {} + + function test_Revert_NotBoundTokenOut() public view {} + + function test_Returns_SpotPrice() public view {} + + function test_Revert_Reentrancy() public view {} +} + +contract BPool_Unit_JoinPool is Base { + function test_Revert_NotFinalized() public view {} + + function test_Revert_MathApprox() public view {} + + function test_Revert_TokenArrayMathApprox() public view {} + + function test_Revert_TokenArrayLimitIn() public view {} + + function test_Revert_Reentrancy() public view {} + + function test_Set_TokenArrayBalance() public view {} + + function test_Emit_TokenArrayLogJoin() public view {} + + function test_Pull_TokenArrayTokenAmountIn() public view {} + + function test_Mint_PoolShare() public view {} + + function test_Push_PoolShare() public view {} + + function test_Emit_LogCall() public view {} +} + +contract BPool_Unit_ExitPool is Base { + function test_Revert_NotFinalized() public view {} + + function test_Revert_MathApprox() public view {} + + function test_Pull_PoolShare() public view {} + + function test_Push_PoolShare() public view {} + + function test_Burn_PoolShare() public view {} + + function test_Revert_TokenArrayMathApprox() public view {} + + function test_Revert_TokenArrayLimitOut() public view {} + + function test_Revert_Reentrancy() public view {} + + function test_Set_TokenArrayBalance() public view {} + + function test_Emit_TokenArrayLogExit() public view {} + + function test_Push_TokenArrayTokenAmountOut() public view {} + + function test_Emit_LogCall() public view {} +} + +contract BPool_Unit_SwapExactAmountIn is Base { + function test_Revert_NotBoundTokenIn() public view {} + + function test_Revert_NotBoundTokenOut() public view {} + + function test_Revert_NotPublic() public view {} + + function test_Revert_MaxInRatio() public view {} + + function test_Revert_BadLimitPrice() public view {} + + function test_Revert_LimitOut() public view {} + + function test_Revert_Reentrancy() public view {} + + function test_Set_InRecord() public view {} + + function test_Set_OutRecord() public view {} + + function test_Revert_MathApprox() public view {} + + function test_Revert_LimitPrice() public view {} + + function test_Revert_MathApprox2() public view {} + + function test_Emit_LogSwap() public view {} + + function test_Pull_TokenAmountIn() public view {} + + function test_Push_TokenAmountOut() public view {} + + function test_Returns_AmountAndPrice() public view {} + + function test_Emit_LogCall() public view {} +} + +contract BPool_Unit_SwapExactAmountOut is Base { + function test_Revert_NotBoundTokenIn() public view {} + + function test_Revert_NotBoundTokenOut() public view {} + + function test_Revert_NotPublic() public view {} + + function test_Revert_MaxOutRatio() public view {} + + function test_Revert_BadLimitPrice() public view {} + + function test_Revert_LimitIn() public view {} + + function test_Revert_Reentrancy() public view {} + + function test_Set_InRecord() public view {} + + function test_Set_OutRecord() public view {} + + function test_Revert_MathApprox() public view {} + + function test_Revert_LimitPrice() public view {} + + function test_Revert_MathApprox2() public view {} + + function test_Emit_LogSwap() public view {} + + function test_Pull_TokenAmountIn() public view {} + + function test_Push_TokenAmountOut() public view {} + + function test_Returns_AmountAndPrice() public view {} + + function test_Emit_LogCall() public view {} +} + +contract BPool_Unit_JoinswapExternAmountIn is Base { + function test_Revert_NotFinalized() public view {} + + function test_Revert_NotBound() public view {} + + function test_Revert_MaxInRatio() public view {} + + function test_Revert_LimitOut() public view {} + + function test_Revert_Reentrancy() public view {} + + function test_Set_Balance() public view {} + + function test_Emit_LogJoin() public view {} + + function test_Mint_PoolShare() public view {} + + function test_Push_PoolShare() public view {} + + function test_Pull_Underlying() public view {} + + function test_Returns_PoolAmountOut() public view {} + + function test_Emit_LogCall() public view {} +} + +contract BPool_Unit_JoinswapExternAmountOut is Base { + function test_Revert_NotFinalized() public view {} + + function test_Revert_NotBound() public view {} + + function test_Revert_MaxApprox() public view {} + + function test_Revert_LimitIn() public view {} + + function test_Revert_MaxInRatio() public view {} + + function test_Revert_Reentrancy() public view {} + + function test_Set_Balance() public view {} + + function test_Emit_LogJoin() public view {} + + function test_Mint_PoolShare() public view {} + + function test_Push_PoolShare() public view {} + + function test_Pull_Underlying() public view {} + + function test_Returns_TokenAmountIn() public view {} + + function test_Emit_LogCall() public view {} +} + +contract BPool_Unit_ExitswapPoolAmountIn is Base { + function test_Revert_NotFinalized() public view {} + + function test_Revert_NotBound() public view {} + + function test_Revert_LimitOut() public view {} + + function test_Revert_MaxOutRatio() public view {} + + function test_Revert_Reentrancy() public view {} + + function test_Set_Balance() public view {} + + function test_Emit_LogExit() public view {} + + function test_Pull_PoolShare() public view {} + + function test_Burn_PoolShare() public view {} + + function test_Push_PoolShare() public view {} + + function test_Push_Underlying() public view {} + + function test_Returns_TokenAmountOut() public view {} + + function test_Emit_LogCall() public view {} +} + +contract BPool_Unit_ExitswapPoolAmountOut is Base { + function test_Revert_NotFinalized() public view {} + + function test_Revert_NotBound() public view {} + + function test_Revert_MaxOutRatio() public view {} + + function test_Revert_MathApprox() public view {} + + function test_Revert_LimitIn() public view {} + + function test_Revert_Reentrancy() public view {} + + function test_Set_Balance() public view {} + + function test_Emit_LogExit() public view {} + + function test_Pull_PoolShare() public view {} + + function test_Burn_PoolShare() public view {} + + function test_Push_PoolShare() public view {} + + function test_Push_Underlying() public view {} + + function test_Returns_PoolAmountIn() public view {} + + function test_Emit_LogCall() public view {} +} From 84e9c29bca8ef24534bddced1d6c0fb528f39b49 Mon Sep 17 00:00:00 2001 From: 0xAustrian Date: Mon, 6 May 2024 17:01:48 -0300 Subject: [PATCH 03/18] test: add happy path for joinPool --- package.json | 2 +- test/unit/BPool.t.sol | 81 ++++++++++++++++++++++++++++++++++++++++--- yarn.lock | 6 ++-- 3 files changed, 80 insertions(+), 9 deletions(-) diff --git a/package.json b/package.json index 9111d0e1..c1dc0bba 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,7 @@ "package.json": "sort-package-json" }, "dependencies": { - "solmate": "github:transmissions11/solmate#a9e3ea2" + "solmate": "github:transmissions11/solmate#c892309" }, "devDependencies": { "@commitlint/cli": "19.3.0", diff --git a/test/unit/BPool.t.sol b/test/unit/BPool.t.sol index 1c9ecb06..a741e66b 100644 --- a/test/unit/BPool.t.sol +++ b/test/unit/BPool.t.sol @@ -1,10 +1,68 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.23; -import {Test} from 'forge-std/Test.sol'; - -abstract contract Base is Test { - function setUp() public {} +import {BConst} from 'contracts/BConst.sol'; +import {BPool} from 'contracts/BPool.sol'; +import {IERC20} from 'contracts/BToken.sol'; +import {Test, console} from 'forge-std/Test.sol'; +import {LibString} from 'solmate/utils/LibString.sol'; + +abstract contract Base is Test, BConst { + using LibString for *; + + BPool public bPool; + address[8] public tokens; + + function setUp() public { + bPool = new BPool(); + + // Create fake tokens + for (uint256 i = 0; i < tokens.length; i++) { + tokens[i] = makeAddr(i.toString()); + } + } + + function _preparePool( + BPool _pool, + address[8] memory _tokens, + uint256[8] memory _balance, + uint256[8] memory _denorm + ) internal { + // Create mocks + for (uint256 i = 0; i < _tokens.length; i++) { + vm.mockCall(_tokens[i], abi.encodeWithSelector(IERC20(_tokens[i]).transfer.selector), abi.encode(true)); + vm.mockCall(_tokens[i], abi.encodeWithSelector(IERC20(_tokens[i]).transferFrom.selector), abi.encode(true)); + } + + // Set tokens + for (uint256 i = 0; i < _tokens.length; i++) { + vm.prank(_pool.getController()); + _pool.bind(_tokens[i], _balance[i], _denorm[i]); + } + + // Set public swap + vm.store( + address(_pool), + bytes32(uint256(6)), + bytes32(uint256(0x0000000000000000000000010000000000000000000000000000000000000000)) + ); + // Set finalize + vm.store(address(_pool), bytes32(uint256(8)), bytes32(uint256(1))); + // Set totalSupply + vm.store(address(_pool), bytes32(uint256(2)), bytes32(INIT_POOL_SUPPLY)); + } + + function _assumeHappyPath( + uint256[8] memory _balance, + uint256[8] memory _denorm + ) internal view returns (uint256[8] memory, uint256[8] memory) { + for (uint256 i = 0; i < _balance.length; i++) { + _balance[i] = bound(_balance[i], MIN_BALANCE, MIN_BALANCE * 1000000); // TODO: found a better max + _denorm[i] = bound(_denorm[i], MIN_WEIGHT, MAX_WEIGHT / 8); // Div by the max number of tokens + } + + return (_balance, _denorm); + } } contract BPool_Unit_Constructor is Base { @@ -252,6 +310,19 @@ contract BPool_Unit_GetSpotPriceSansFee is Base { } contract BPool_Unit_JoinPool is Base { + function test_HappyPath(uint256 _poolAmountOut, uint256[8] memory _balance, uint256[8] memory _denorm) public { + vm.assume(_poolAmountOut > INIT_POOL_SUPPLY); + vm.assume(_poolAmountOut < type(uint256).max / BONE); + + uint256[] memory maxAmountsIn = new uint256[](tokens.length); + for (uint256 i = 0; i < tokens.length; i++) { maxAmountsIn[i] = type(uint256).max; } // Using max possible amounts + + (uint256[8] memory __balance, uint256[8] memory __denorm) = _assumeHappyPath(_balance, _denorm); + _preparePool(bPool, tokens, __balance, __denorm); + + bPool.joinPool(_poolAmountOut, maxAmountsIn); + } + function test_Revert_NotFinalized() public view {} function test_Revert_MathApprox() public view {} @@ -463,7 +534,7 @@ contract BPool_Unit_ExitswapPoolAmountOut is Base { function test_Revert_MaxOutRatio() public view {} function test_Revert_MathApprox() public view {} - + function test_Revert_LimitIn() public view {} function test_Revert_Reentrancy() public view {} diff --git a/yarn.lock b/yarn.lock index 14642107..b7c9332e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1713,9 +1713,9 @@ solc@0.8.24: optionalDependencies: prettier "^2.8.3" -"solmate@github:transmissions11/solmate#a9e3ea2": - version "6.0.0" - resolved "https://codeload.github.com/transmissions11/solmate/tar.gz/a9e3ea26a2dc73bfa87f0cb189687d029028e0c5" +"solmate@github:transmissions11/solmate#c892309": + version "6.2.0" + resolved "https://codeload.github.com/transmissions11/solmate/tar.gz/c892309933b25c03d32b1b0d674df7ae292ba925" sort-object-keys@^1.1.3: version "1.1.3" From 9fda7298f23fc69beaf237edc349ffaf268b685c Mon Sep 17 00:00:00 2001 From: 0xAustrian Date: Tue, 7 May 2024 16:29:00 -0300 Subject: [PATCH 04/18] test: wip --- foundry.toml | 3 +- test/unit/BPool.t.sol | 133 ++++++++++++++++++++++++++---------------- 2 files changed, 85 insertions(+), 51 deletions(-) diff --git a/foundry.toml b/foundry.toml index 1e9d2ee4..2aed72ce 100644 --- a/foundry.toml +++ b/foundry.toml @@ -25,7 +25,8 @@ out = 'out-via-ir' src = 'src/interfaces/' [fuzz] -runs = 1000 +runs = 500 +max_test_rejects = 1000000 [rpc_endpoints] mainnet = "${MAINNET_RPC}" diff --git a/test/unit/BPool.t.sol b/test/unit/BPool.t.sol index a741e66b..862bcfc7 100644 --- a/test/unit/BPool.t.sol +++ b/test/unit/BPool.t.sol @@ -11,7 +11,7 @@ abstract contract Base is Test, BConst { using LibString for *; BPool public bPool; - address[8] public tokens; + address[2] public tokens; function setUp() public { bPool = new BPool(); @@ -21,48 +21,6 @@ abstract contract Base is Test, BConst { tokens[i] = makeAddr(i.toString()); } } - - function _preparePool( - BPool _pool, - address[8] memory _tokens, - uint256[8] memory _balance, - uint256[8] memory _denorm - ) internal { - // Create mocks - for (uint256 i = 0; i < _tokens.length; i++) { - vm.mockCall(_tokens[i], abi.encodeWithSelector(IERC20(_tokens[i]).transfer.selector), abi.encode(true)); - vm.mockCall(_tokens[i], abi.encodeWithSelector(IERC20(_tokens[i]).transferFrom.selector), abi.encode(true)); - } - - // Set tokens - for (uint256 i = 0; i < _tokens.length; i++) { - vm.prank(_pool.getController()); - _pool.bind(_tokens[i], _balance[i], _denorm[i]); - } - - // Set public swap - vm.store( - address(_pool), - bytes32(uint256(6)), - bytes32(uint256(0x0000000000000000000000010000000000000000000000000000000000000000)) - ); - // Set finalize - vm.store(address(_pool), bytes32(uint256(8)), bytes32(uint256(1))); - // Set totalSupply - vm.store(address(_pool), bytes32(uint256(2)), bytes32(INIT_POOL_SUPPLY)); - } - - function _assumeHappyPath( - uint256[8] memory _balance, - uint256[8] memory _denorm - ) internal view returns (uint256[8] memory, uint256[8] memory) { - for (uint256 i = 0; i < _balance.length; i++) { - _balance[i] = bound(_balance[i], MIN_BALANCE, MIN_BALANCE * 1000000); // TODO: found a better max - _denorm[i] = bound(_denorm[i], MIN_WEIGHT, MAX_WEIGHT / 8); // Div by the max number of tokens - } - - return (_balance, _denorm); - } } contract BPool_Unit_Constructor is Base { @@ -310,17 +268,92 @@ contract BPool_Unit_GetSpotPriceSansFee is Base { } contract BPool_Unit_JoinPool is Base { - function test_HappyPath(uint256 _poolAmountOut, uint256[8] memory _balance, uint256[8] memory _denorm) public { - vm.assume(_poolAmountOut > INIT_POOL_SUPPLY); - vm.assume(_poolAmountOut < type(uint256).max / BONE); + struct FuzzScenario { + uint256 poolAmountOut; + uint256 balance0; + uint256 balance1; + // uint256 balance2; + // uint256 balance3; + // uint256 balance4; + // uint256 balance5; + // uint256 balance6; + // uint256 balance7; + // uint256[3] balance; + } + + function _setValues(FuzzScenario memory _fuzz) internal { + // Create mocks + for (uint256 i = 0; i < tokens.length; i++) { + vm.mockCall(tokens[i], abi.encodeWithSelector(IERC20(tokens[i]).transfer.selector), abi.encode(true)); + vm.mockCall(tokens[i], abi.encodeWithSelector(IERC20(tokens[i]).transferFrom.selector), abi.encode(true)); + } + + // Set tokens + bytes memory _arraySlot = abi.encode(9); + bytes32 _hashArraySlot = keccak256(_arraySlot); + vm.store(address(bPool), bytes32(_arraySlot), bytes32(tokens.length)); // write length + for (uint256 i = 0; i < tokens.length; i++) { + vm.store(address(bPool), bytes32(uint256(_hashArraySlot) + i), bytes32(abi.encode(tokens[i]))); // write token + } + + // Set balances + for (uint256 i = 0; i < tokens.length; i++) { + bytes32 _slot = keccak256(abi.encode(tokens[i], 10)); // mapping is found at slot 10 + vm.store(address(bPool), bytes32(uint256(_slot) + 0), bytes32(abi.encode(69))); // bound + vm.store(address(bPool), bytes32(uint256(_slot) + 3), bytes32(abi.encode(69))); // balance + } + + // Set public swap + vm.store( + address(bPool), + bytes32(uint256(6)), + bytes32(uint256(0x0000000000000000000000010000000000000000000000000000000000000000)) + ); + // Set finalize + vm.store(address(bPool), bytes32(uint256(8)), bytes32(uint256(1))); + // Set totalSupply + vm.store(address(bPool), bytes32(uint256(2)), bytes32(INIT_POOL_SUPPLY)); + } + + function _assumeHappyPath(FuzzScenario memory _fuzz) internal view { + vm.assume(_fuzz.poolAmountOut > INIT_POOL_SUPPLY); + vm.assume(_fuzz.poolAmountOut < type(uint256).max / BONE); + + // for (uint256 i = 0; i < _fuzz.balance.length; i++) { + // vm.assume(_fuzz.balance[i] > MIN_BALANCE); + // vm.assume(_fuzz.balance[i] < MIN_BALANCE * 1000000); + // } + + vm.assume(_fuzz.balance0 > MIN_BALANCE); + vm.assume(_fuzz.balance0 < MIN_BALANCE * 1000000); // TODO: found a better max + vm.assume(_fuzz.balance1 > MIN_BALANCE); + vm.assume(_fuzz.balance1 < MIN_BALANCE * 1000000); // TODO: found a better max + // vm.assume(_fuzz.balance2 < MIN_BALANCE * 1000000); // TODO: found a better max + // vm.assume(_fuzz.balance2 > MIN_BALANCE); + // vm.assume(_fuzz.balance3 < MIN_BALANCE * 1000000); // TODO: found a better max + // vm.assume(_fuzz.balance3 > MIN_BALANCE); + // vm.assume(_fuzz.balance4 < MIN_BALANCE * 1000000); // TODO: found a better max + // vm.assume(_fuzz.balance4 > MIN_BALANCE); + // vm.assume(_fuzz.balance5 < MIN_BALANCE * 1000000); // TODO: found a better max + // vm.assume(_fuzz.balance5 > MIN_BALANCE); + // vm.assume(_fuzz.balance6 < MIN_BALANCE * 1000000); // TODO: found a better max + // vm.assume(_fuzz.balance6 > MIN_BALANCE); + // vm.assume(_fuzz.balance7 < MIN_BALANCE * 1000000); // TODO: found a better max + // vm.assume(_fuzz.balance7 > MIN_BALANCE); + } + + modifier happyPath(FuzzScenario memory _fuzz) { + _assumeHappyPath(_fuzz); + _setValues(_fuzz); + _; + } + + function test_HappyPath(FuzzScenario memory _fuzz) public happyPath(_fuzz) { uint256[] memory maxAmountsIn = new uint256[](tokens.length); for (uint256 i = 0; i < tokens.length; i++) { maxAmountsIn[i] = type(uint256).max; } // Using max possible amounts - (uint256[8] memory __balance, uint256[8] memory __denorm) = _assumeHappyPath(_balance, _denorm); - _preparePool(bPool, tokens, __balance, __denorm); - - bPool.joinPool(_poolAmountOut, maxAmountsIn); + bPool.joinPool(_fuzz.poolAmountOut, maxAmountsIn); } function test_Revert_NotFinalized() public view {} From a2dad8406a01db2222c35569b9293fae483ab9b5 Mon Sep 17 00:00:00 2001 From: 0xAustrian Date: Tue, 7 May 2024 16:33:00 -0300 Subject: [PATCH 05/18] test: tweak assume --- foundry.toml | 2 +- test/unit/BPool.t.sol | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/foundry.toml b/foundry.toml index 2aed72ce..f3a782d6 100644 --- a/foundry.toml +++ b/foundry.toml @@ -25,7 +25,7 @@ out = 'out-via-ir' src = 'src/interfaces/' [fuzz] -runs = 500 +runs = 10000 max_test_rejects = 1000000 [rpc_endpoints] diff --git a/test/unit/BPool.t.sol b/test/unit/BPool.t.sol index 862bcfc7..1b126824 100644 --- a/test/unit/BPool.t.sol +++ b/test/unit/BPool.t.sol @@ -326,9 +326,9 @@ contract BPool_Unit_JoinPool is Base { // } vm.assume(_fuzz.balance0 > MIN_BALANCE); - vm.assume(_fuzz.balance0 < MIN_BALANCE * 1000000); // TODO: found a better max + vm.assume(_fuzz.balance0 < type(uint256).max / BONE); // TODO: found a better max vm.assume(_fuzz.balance1 > MIN_BALANCE); - vm.assume(_fuzz.balance1 < MIN_BALANCE * 1000000); // TODO: found a better max + vm.assume(_fuzz.balance1 < type(uint256).max / BONE); // TODO: found a better max // vm.assume(_fuzz.balance2 < MIN_BALANCE * 1000000); // TODO: found a better max // vm.assume(_fuzz.balance2 > MIN_BALANCE); // vm.assume(_fuzz.balance3 < MIN_BALANCE * 1000000); // TODO: found a better max From 79382adc6e6f579a9b01ca892faace8329cbdeef Mon Sep 17 00:00:00 2001 From: 0xAustrian Date: Tue, 7 May 2024 20:52:56 -0300 Subject: [PATCH 06/18] test: wip, improve fuzzing --- foundry.toml | 4 ++-- test/unit/BPool.t.sol | 49 ++++++++++++++----------------------------- 2 files changed, 18 insertions(+), 35 deletions(-) diff --git a/foundry.toml b/foundry.toml index f3a782d6..71cfb5de 100644 --- a/foundry.toml +++ b/foundry.toml @@ -25,8 +25,8 @@ out = 'out-via-ir' src = 'src/interfaces/' [fuzz] -runs = 10000 -max_test_rejects = 1000000 +runs = 1000 +max_test_rejects = 10000000 [rpc_endpoints] mainnet = "${MAINNET_RPC}" diff --git a/test/unit/BPool.t.sol b/test/unit/BPool.t.sol index 1b126824..b0c6dfff 100644 --- a/test/unit/BPool.t.sol +++ b/test/unit/BPool.t.sol @@ -11,7 +11,7 @@ abstract contract Base is Test, BConst { using LibString for *; BPool public bPool; - address[2] public tokens; + address[5] public tokens; function setUp() public { bPool = new BPool(); @@ -271,15 +271,7 @@ contract BPool_Unit_JoinPool is Base { struct FuzzScenario { uint256 poolAmountOut; - uint256 balance0; - uint256 balance1; - // uint256 balance2; - // uint256 balance3; - // uint256 balance4; - // uint256 balance5; - // uint256 balance6; - // uint256 balance7; - // uint256[3] balance; + uint256[5] balance; } function _setValues(FuzzScenario memory _fuzz) internal { @@ -300,8 +292,8 @@ contract BPool_Unit_JoinPool is Base { // Set balances for (uint256 i = 0; i < tokens.length; i++) { bytes32 _slot = keccak256(abi.encode(tokens[i], 10)); // mapping is found at slot 10 - vm.store(address(bPool), bytes32(uint256(_slot) + 0), bytes32(abi.encode(69))); // bound - vm.store(address(bPool), bytes32(uint256(_slot) + 3), bytes32(abi.encode(69))); // balance + vm.store(address(bPool), bytes32(uint256(_slot) + 0), bytes32(abi.encode(1))); // bound + vm.store(address(bPool), bytes32(uint256(_slot) + 3), bytes32(abi.encode(_fuzz.balance[i]))); // balance } // Set public swap @@ -320,27 +312,18 @@ contract BPool_Unit_JoinPool is Base { vm.assume(_fuzz.poolAmountOut > INIT_POOL_SUPPLY); vm.assume(_fuzz.poolAmountOut < type(uint256).max / BONE); - // for (uint256 i = 0; i < _fuzz.balance.length; i++) { - // vm.assume(_fuzz.balance[i] > MIN_BALANCE); - // vm.assume(_fuzz.balance[i] < MIN_BALANCE * 1000000); - // } - - vm.assume(_fuzz.balance0 > MIN_BALANCE); - vm.assume(_fuzz.balance0 < type(uint256).max / BONE); // TODO: found a better max - vm.assume(_fuzz.balance1 > MIN_BALANCE); - vm.assume(_fuzz.balance1 < type(uint256).max / BONE); // TODO: found a better max - // vm.assume(_fuzz.balance2 < MIN_BALANCE * 1000000); // TODO: found a better max - // vm.assume(_fuzz.balance2 > MIN_BALANCE); - // vm.assume(_fuzz.balance3 < MIN_BALANCE * 1000000); // TODO: found a better max - // vm.assume(_fuzz.balance3 > MIN_BALANCE); - // vm.assume(_fuzz.balance4 < MIN_BALANCE * 1000000); // TODO: found a better max - // vm.assume(_fuzz.balance4 > MIN_BALANCE); - // vm.assume(_fuzz.balance5 < MIN_BALANCE * 1000000); // TODO: found a better max - // vm.assume(_fuzz.balance5 > MIN_BALANCE); - // vm.assume(_fuzz.balance6 < MIN_BALANCE * 1000000); // TODO: found a better max - // vm.assume(_fuzz.balance6 > MIN_BALANCE); - // vm.assume(_fuzz.balance7 < MIN_BALANCE * 1000000); // TODO: found a better max - // vm.assume(_fuzz.balance7 > MIN_BALANCE); + uint _ratio = _fuzz.poolAmountOut / INIT_POOL_SUPPLY; + + for (uint256 i = 0; i < _fuzz.balance.length; i++) { + vm.assume(_fuzz.balance[i] > MIN_BALANCE); + + uint _maxTokenAmountIn = type(uint256).max / _ratio; + vm.assume(_fuzz.balance[i] < _maxTokenAmountIn); // L272 + + uint _tokenAmountIn = _ratio * _fuzz.balance[i]; + // vm.assume(_tokenAmountIn < _maxTokenAmountIn); + vm.assume(_fuzz.balance[i] < type(uint256).max - _maxTokenAmountIn); + } } modifier happyPath(FuzzScenario memory _fuzz) { From 7ddb7005ea792357c4736c55a7ad4d85a98655b7 Mon Sep 17 00:00:00 2001 From: 0xAustrian Date: Wed, 8 May 2024 09:19:13 -0300 Subject: [PATCH 07/18] test: improve assume --- test/unit/BPool.t.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/unit/BPool.t.sol b/test/unit/BPool.t.sol index b0c6dfff..daba306a 100644 --- a/test/unit/BPool.t.sol +++ b/test/unit/BPool.t.sol @@ -321,8 +321,8 @@ contract BPool_Unit_JoinPool is Base { vm.assume(_fuzz.balance[i] < _maxTokenAmountIn); // L272 uint _tokenAmountIn = _ratio * _fuzz.balance[i]; - // vm.assume(_tokenAmountIn < _maxTokenAmountIn); - vm.assume(_fuzz.balance[i] < type(uint256).max - _maxTokenAmountIn); + vm.assume(_tokenAmountIn < type(uint256).max - _fuzz.balance[i]); + vm.assume(_fuzz.balance[i] < type(uint256).max - _tokenAmountIn); } } From ea089f2eb3759912a87684c9abc590e0548f3ee7 Mon Sep 17 00:00:00 2001 From: 0xAustrian Date: Wed, 8 May 2024 11:35:29 -0300 Subject: [PATCH 08/18] test: assumes are working now --- test/unit/BPool.t.sol | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/test/unit/BPool.t.sol b/test/unit/BPool.t.sol index daba306a..27a49048 100644 --- a/test/unit/BPool.t.sol +++ b/test/unit/BPool.t.sol @@ -312,17 +312,15 @@ contract BPool_Unit_JoinPool is Base { vm.assume(_fuzz.poolAmountOut > INIT_POOL_SUPPLY); vm.assume(_fuzz.poolAmountOut < type(uint256).max / BONE); - uint _ratio = _fuzz.poolAmountOut / INIT_POOL_SUPPLY; + uint _poolAmountOutTimesBONE = _fuzz.poolAmountOut * BONE; // bdiv uses '* BONE' + + uint _ratio = _poolAmountOutTimesBONE / INIT_POOL_SUPPLY; for (uint256 i = 0; i < _fuzz.balance.length; i++) { vm.assume(_fuzz.balance[i] > MIN_BALANCE); uint _maxTokenAmountIn = type(uint256).max / _ratio; vm.assume(_fuzz.balance[i] < _maxTokenAmountIn); // L272 - - uint _tokenAmountIn = _ratio * _fuzz.balance[i]; - vm.assume(_tokenAmountIn < type(uint256).max - _fuzz.balance[i]); - vm.assume(_fuzz.balance[i] < type(uint256).max - _tokenAmountIn); } } From bfbb34be21de549b6de8306686af5d0ac9f59c81 Mon Sep 17 00:00:00 2001 From: 0xAustrian Date: Wed, 8 May 2024 11:41:32 -0300 Subject: [PATCH 09/18] test: increase tokens to 8 --- test/unit/BPool.t.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/unit/BPool.t.sol b/test/unit/BPool.t.sol index 27a49048..663fdcc3 100644 --- a/test/unit/BPool.t.sol +++ b/test/unit/BPool.t.sol @@ -11,7 +11,7 @@ abstract contract Base is Test, BConst { using LibString for *; BPool public bPool; - address[5] public tokens; + address[8] public tokens; function setUp() public { bPool = new BPool(); @@ -271,7 +271,7 @@ contract BPool_Unit_JoinPool is Base { struct FuzzScenario { uint256 poolAmountOut; - uint256[5] balance; + uint256[8] balance; } function _setValues(FuzzScenario memory _fuzz) internal { From 8d467ddb4a1d1175013e3dc218dceba9ec03f83f Mon Sep 17 00:00:00 2001 From: 0xAustrian Date: Wed, 8 May 2024 13:19:43 -0300 Subject: [PATCH 10/18] test: finish happyPath for joinPool --- test/unit/BPool.t.sol | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/test/unit/BPool.t.sol b/test/unit/BPool.t.sol index 663fdcc3..cd50b0df 100644 --- a/test/unit/BPool.t.sol +++ b/test/unit/BPool.t.sol @@ -10,8 +10,10 @@ import {LibString} from 'solmate/utils/LibString.sol'; abstract contract Base is Test, BConst { using LibString for *; + uint256 public constant TOKENS_AMOUNT = 5; + BPool public bPool; - address[8] public tokens; + address[TOKENS_AMOUNT] public tokens; function setUp() public { bPool = new BPool(); @@ -268,10 +270,10 @@ contract BPool_Unit_GetSpotPriceSansFee is Base { } contract BPool_Unit_JoinPool is Base { - struct FuzzScenario { uint256 poolAmountOut; - uint256[8] balance; + uint256 initPoolSupply; + uint256[TOKENS_AMOUNT] balance; } function _setValues(FuzzScenario memory _fuzz) internal { @@ -305,22 +307,21 @@ contract BPool_Unit_JoinPool is Base { // Set finalize vm.store(address(bPool), bytes32(uint256(8)), bytes32(uint256(1))); // Set totalSupply - vm.store(address(bPool), bytes32(uint256(2)), bytes32(INIT_POOL_SUPPLY)); + vm.store(address(bPool), bytes32(uint256(2)), bytes32(_fuzz.initPoolSupply)); } function _assumeHappyPath(FuzzScenario memory _fuzz) internal view { - vm.assume(_fuzz.poolAmountOut > INIT_POOL_SUPPLY); + vm.assume(_fuzz.initPoolSupply >= INIT_POOL_SUPPLY); + vm.assume(_fuzz.poolAmountOut >= _fuzz.initPoolSupply); vm.assume(_fuzz.poolAmountOut < type(uint256).max / BONE); - uint _poolAmountOutTimesBONE = _fuzz.poolAmountOut * BONE; // bdiv uses '* BONE' - - uint _ratio = _poolAmountOutTimesBONE / INIT_POOL_SUPPLY; + uint256 _poolAmountOutTimesBONE = _fuzz.poolAmountOut * BONE; // bdiv uses '* BONE' + uint256 _ratio = _poolAmountOutTimesBONE / _fuzz.initPoolSupply; + uint _maxTokenAmountIn = type(uint256).max / _ratio; for (uint256 i = 0; i < _fuzz.balance.length; i++) { - vm.assume(_fuzz.balance[i] > MIN_BALANCE); - - uint _maxTokenAmountIn = type(uint256).max / _ratio; - vm.assume(_fuzz.balance[i] < _maxTokenAmountIn); // L272 + vm.assume(_fuzz.balance[i] >= MIN_BALANCE); + vm.assume(_fuzz.balance[i] <= _maxTokenAmountIn); // L272 } } @@ -332,7 +333,9 @@ contract BPool_Unit_JoinPool is Base { function test_HappyPath(FuzzScenario memory _fuzz) public happyPath(_fuzz) { uint256[] memory maxAmountsIn = new uint256[](tokens.length); - for (uint256 i = 0; i < tokens.length; i++) { maxAmountsIn[i] = type(uint256).max; } // Using max possible amounts + for (uint256 i = 0; i < tokens.length; i++) { + maxAmountsIn[i] = type(uint256).max; + } // Using max possible amounts bPool.joinPool(_fuzz.poolAmountOut, maxAmountsIn); } From 92cdff712ce88fb3e3f30738d2f57bc53a8889f9 Mon Sep 17 00:00:00 2001 From: 0xAustrian Date: Wed, 8 May 2024 13:23:51 -0300 Subject: [PATCH 11/18] fix: lint --- test/unit/BPool.t.sol | 78 +++++++++++++++++++++---------------------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/test/unit/BPool.t.sol b/test/unit/BPool.t.sol index cd50b0df..9e99ed7b 100644 --- a/test/unit/BPool.t.sol +++ b/test/unit/BPool.t.sol @@ -4,7 +4,7 @@ pragma solidity 0.8.23; import {BConst} from 'contracts/BConst.sol'; import {BPool} from 'contracts/BPool.sol'; import {IERC20} from 'contracts/BToken.sol'; -import {Test, console} from 'forge-std/Test.sol'; +import {Test} from 'forge-std/Test.sol'; import {LibString} from 'solmate/utils/LibString.sol'; abstract contract Base is Test, BConst { @@ -276,6 +276,43 @@ contract BPool_Unit_JoinPool is Base { uint256[TOKENS_AMOUNT] balance; } + function test_HappyPath(FuzzScenario memory _fuzz) public happyPath(_fuzz) { + uint256[] memory maxAmountsIn = new uint256[](tokens.length); + for (uint256 i = 0; i < tokens.length; i++) { + maxAmountsIn[i] = type(uint256).max; + } // Using max possible amounts + + bPool.joinPool(_fuzz.poolAmountOut, maxAmountsIn); + } + + function test_Revert_NotFinalized() public view {} + + function test_Revert_MathApprox() public view {} + + function test_Revert_TokenArrayMathApprox() public view {} + + function test_Revert_TokenArrayLimitIn() public view {} + + function test_Revert_Reentrancy() public view {} + + function test_Set_TokenArrayBalance() public view {} + + function test_Emit_TokenArrayLogJoin() public view {} + + function test_Pull_TokenArrayTokenAmountIn() public view {} + + function test_Mint_PoolShare() public view {} + + function test_Push_PoolShare() public view {} + + function test_Emit_LogCall() public view {} + + modifier happyPath(FuzzScenario memory _fuzz) { + _assumeHappyPath(_fuzz); + _setValues(_fuzz); + _; + } + function _setValues(FuzzScenario memory _fuzz) internal { // Create mocks for (uint256 i = 0; i < tokens.length; i++) { @@ -317,50 +354,13 @@ contract BPool_Unit_JoinPool is Base { uint256 _poolAmountOutTimesBONE = _fuzz.poolAmountOut * BONE; // bdiv uses '* BONE' uint256 _ratio = _poolAmountOutTimesBONE / _fuzz.initPoolSupply; - uint _maxTokenAmountIn = type(uint256).max / _ratio; + uint256 _maxTokenAmountIn = type(uint256).max / _ratio; for (uint256 i = 0; i < _fuzz.balance.length; i++) { vm.assume(_fuzz.balance[i] >= MIN_BALANCE); vm.assume(_fuzz.balance[i] <= _maxTokenAmountIn); // L272 } } - - modifier happyPath(FuzzScenario memory _fuzz) { - _assumeHappyPath(_fuzz); - _setValues(_fuzz); - _; - } - - function test_HappyPath(FuzzScenario memory _fuzz) public happyPath(_fuzz) { - uint256[] memory maxAmountsIn = new uint256[](tokens.length); - for (uint256 i = 0; i < tokens.length; i++) { - maxAmountsIn[i] = type(uint256).max; - } // Using max possible amounts - - bPool.joinPool(_fuzz.poolAmountOut, maxAmountsIn); - } - - function test_Revert_NotFinalized() public view {} - - function test_Revert_MathApprox() public view {} - - function test_Revert_TokenArrayMathApprox() public view {} - - function test_Revert_TokenArrayLimitIn() public view {} - - function test_Revert_Reentrancy() public view {} - - function test_Set_TokenArrayBalance() public view {} - - function test_Emit_TokenArrayLogJoin() public view {} - - function test_Pull_TokenArrayTokenAmountIn() public view {} - - function test_Mint_PoolShare() public view {} - - function test_Push_PoolShare() public view {} - - function test_Emit_LogCall() public view {} } contract BPool_Unit_ExitPool is Base { From 0929672f07823c6c9b4beb6e407db4eb3bc5a7c3 Mon Sep 17 00:00:00 2001 From: 0xAustrian Date: Wed, 8 May 2024 16:40:26 -0300 Subject: [PATCH 12/18] test: improve happyPath --- foundry.toml | 2 +- test/unit/BPool.t.sol | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/foundry.toml b/foundry.toml index 71cfb5de..8599d1c0 100644 --- a/foundry.toml +++ b/foundry.toml @@ -26,7 +26,7 @@ src = 'src/interfaces/' [fuzz] runs = 1000 -max_test_rejects = 10000000 +max_test_rejects = 400000 [rpc_endpoints] mainnet = "${MAINNET_RPC}" diff --git a/test/unit/BPool.t.sol b/test/unit/BPool.t.sol index 9e99ed7b..544964c0 100644 --- a/test/unit/BPool.t.sol +++ b/test/unit/BPool.t.sol @@ -10,7 +10,7 @@ import {LibString} from 'solmate/utils/LibString.sol'; abstract contract Base is Test, BConst { using LibString for *; - uint256 public constant TOKENS_AMOUNT = 5; + uint256 public constant TOKENS_AMOUNT = 3; BPool public bPool; address[TOKENS_AMOUNT] public tokens; @@ -352,8 +352,7 @@ contract BPool_Unit_JoinPool is Base { vm.assume(_fuzz.poolAmountOut >= _fuzz.initPoolSupply); vm.assume(_fuzz.poolAmountOut < type(uint256).max / BONE); - uint256 _poolAmountOutTimesBONE = _fuzz.poolAmountOut * BONE; // bdiv uses '* BONE' - uint256 _ratio = _poolAmountOutTimesBONE / _fuzz.initPoolSupply; + uint256 _ratio = (_fuzz.poolAmountOut * BONE) / _fuzz.initPoolSupply; // bdiv uses '* BONE' uint256 _maxTokenAmountIn = type(uint256).max / _ratio; for (uint256 i = 0; i < _fuzz.balance.length; i++) { From 08fd10661b4493f223b54725ad9078263ec13d7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wei=C3=9Fer=20Hase?= Date: Wed, 8 May 2024 21:41:52 +0200 Subject: [PATCH 13/18] fix: avoid empty tests from running --- test/unit/BPool.t.sol | 416 +++++++++++++++++++++--------------------- 1 file changed, 208 insertions(+), 208 deletions(-) diff --git a/test/unit/BPool.t.sol b/test/unit/BPool.t.sol index 9e99ed7b..c5955121 100644 --- a/test/unit/BPool.t.sol +++ b/test/unit/BPool.t.sol @@ -26,247 +26,247 @@ abstract contract Base is Test, BConst { } contract BPool_Unit_Constructor is Base { - function test_Deploy() public view {} + function test_Deploy() private view {} } contract BPool_Unit_IsPublicSwap is Base { - function test_Returns_IsPublicSwap() public view {} + function test_Returns_IsPublicSwap() private view {} } contract BPool_Unit_IsFinalized is Base { - function test_Returns_IsFinalized() public view {} + function test_Returns_IsFinalized() private view {} } contract BPool_Unit_IsBound is Base { - function test_Returns_IsBound() public view {} + function test_Returns_IsBound() private view {} - function test_Returns_IsNotBound() public view {} + function test_Returns_IsNotBound() private view {} } contract BPool_Unit_GetNumTokens is Base { - function test_Returns_NumTokens() public view {} + function test_Returns_NumTokens() private view {} } contract BPool_Unit_GetCurrentTokens is Base { - function test_Returns_CurrentTokens() public view {} + function test_Returns_CurrentTokens() private view {} - function test_Revert_Reentrancy() public view {} + function test_Revert_Reentrancy() private view {} } contract BPool_Unit_GetFinalTokens is Base { - function test_Returns_FinalTokens() public view {} + function test_Returns_FinalTokens() private view {} - function test_Revert_Reentrancy() public view {} + function test_Revert_Reentrancy() private view {} - function test_Revert_NotFinalized() public view {} + function test_Revert_NotFinalized() private view {} } contract BPool_Unit_GetDenormalizedWeight is Base { - function test_Returns_DenormalizedWeight() public view {} + function test_Returns_DenormalizedWeight() private view {} - function test_Revert_Reentrancy() public view {} + function test_Revert_Reentrancy() private view {} - function test_Revert_NotBound() public view {} + function test_Revert_NotBound() private view {} } contract BPool_Unit_GetTotalDenormalizedWeight is Base { - function test_Returns_TotalDenormalizedWeight() public view {} + function test_Returns_TotalDenormalizedWeight() private view {} - function test_Revert_Reentrancy() public view {} + function test_Revert_Reentrancy() private view {} } contract BPool_Unit_GetNormalizedWeight is Base { - function test_Returns_NormalizedWeight() public view {} + function test_Returns_NormalizedWeight() private view {} - function test_Revert_Reentrancy() public view {} + function test_Revert_Reentrancy() private view {} - function test_Revert_NotBound() public view {} + function test_Revert_NotBound() private view {} } contract BPool_Unit_GetBalance is Base { - function test_Returns_Balance() public view {} + function test_Returns_Balance() private view {} - function test_Revert_Reentrancy() public view {} + function test_Revert_Reentrancy() private view {} - function test_Revert_NotBound() public view {} + function test_Revert_NotBound() private view {} } contract BPool_Unit_GetSwapFee is Base { - function test_Returns_SwapFee() public view {} + function test_Returns_SwapFee() private view {} - function test_Revert_Reentrancy() public view {} + function test_Revert_Reentrancy() private view {} } contract BPool_Unit_GetController is Base { - function test_Returns_Controller() public view {} + function test_Returns_Controller() private view {} - function test_Revert_Reentrancy() public view {} + function test_Revert_Reentrancy() private view {} } contract BPool_Unit_SetSwapFee is Base { - function test_Revert_Finalized() public view {} + function test_Revert_Finalized() private view {} - function test_Revert_NotController() public view {} + function test_Revert_NotController() private view {} - function test_Revert_MinFee() public view {} + function test_Revert_MinFee() private view {} - function test_Revert_MaxFee() public view {} + function test_Revert_MaxFee() private view {} - function test_Revert_Reentrancy() public view {} + function test_Revert_Reentrancy() private view {} - function test_Set_SwapFee() public view {} + function test_Set_SwapFee() private view {} - function test_Emit_LogCall() public view {} + function test_Emit_LogCall() private view {} } contract BPool_Unit_SetController is Base { - function test_Revert_NotController() public view {} + function test_Revert_NotController() private view {} - function test_Revert_Reentrancy() public view {} + function test_Revert_Reentrancy() private view {} - function test_Set_Controller() public view {} + function test_Set_Controller() private view {} - function test_Emit_LogCall() public view {} + function test_Emit_LogCall() private view {} } contract BPool_Unit_SetPublicSwap is Base { - function test_Revert_Finalized() public view {} + function test_Revert_Finalized() private view {} - function test_Revert_NotController() public view {} + function test_Revert_NotController() private view {} - function test_Revert_Reentrancy() public view {} + function test_Revert_Reentrancy() private view {} - function test_Set_PublicSwap() public view {} + function test_Set_PublicSwap() private view {} - function test_Emit_LogCall() public view {} + function test_Emit_LogCall() private view {} } contract BPool_Unit_Finalize is Base { - function test_Revert_NotController() public view {} + function test_Revert_NotController() private view {} - function test_Revert_Finalized() public view {} + function test_Revert_Finalized() private view {} - function test_Revert_MinTokens() public view {} + function test_Revert_MinTokens() private view {} - function test_Revert_Reentrancy() public view {} + function test_Revert_Reentrancy() private view {} - function test_Set_Finalize() public view {} + function test_Set_Finalize() private view {} - function test_Set_PublicSwap() public view {} + function test_Set_PublicSwap() private view {} - function test_Mint_InitPoolSupply() public view {} + function test_Mint_InitPoolSupply() private view {} - function test_Push_InitPoolSupply() public view {} + function test_Push_InitPoolSupply() private view {} - function test_Emit_LogCall() public view {} + function test_Emit_LogCall() private view {} } contract BPool_Unit_Bind is Base { - function test_Revert_NotController() public view {} + function test_Revert_NotController() private view {} - function test_Revert_IsBound() public view {} + function test_Revert_IsBound() private view {} - function test_Revert_Finalized() public view {} + function test_Revert_Finalized() private view {} - function test_Revert_MaxPoolTokens() public view {} + function test_Revert_MaxPoolTokens() private view {} - function test_Set_Record() public view {} + function test_Set_Record() private view {} - function test_Set_TokenArray() public view {} + function test_Set_TokenArray() private view {} - function test_Emit_LogCall() public view {} + function test_Emit_LogCall() private view {} - function test_Call_Rebind() public view {} + function test_Call_Rebind() private view {} } contract BPool_Unit_Rebind is Base { - function test_Revert_NotController() public view {} + function test_Revert_NotController() private view {} - function test_Revert_NotBound() public view {} + function test_Revert_NotBound() private view {} - function test_Revert_Finalized() public view {} + function test_Revert_Finalized() private view {} - function test_Revert_MinWeight() public view {} + function test_Revert_MinWeight() private view {} - function test_Revert_MaxWeight() public view {} + function test_Revert_MaxWeight() private view {} - function test_Revert_MinBalance() public view {} + function test_Revert_MinBalance() private view {} - function test_Revert_Reentrancy() public view {} + function test_Revert_Reentrancy() private view {} - function test_Set_TotalWeightIfDenormMoreThanOldWeight() public view {} + function test_Set_TotalWeightIfDenormMoreThanOldWeight() private view {} - function test_Set_TotalWeightIfDenormLessThanOldWeight() public view {} + function test_Set_TotalWeightIfDenormLessThanOldWeight() private view {} - function test_Revert_MaxTotalWeight() public view {} + function test_Revert_MaxTotalWeight() private view {} - function test_Set_Denorm() public view {} + function test_Set_Denorm() private view {} - function test_Set_Balance() public view {} + function test_Set_Balance() private view {} - function test_Pull_IfBalanceMoreThanOldBalance() public view {} + function test_Pull_IfBalanceMoreThanOldBalance() private view {} - function test_Push_UnderlyingIfBalanceLessThanOldBalance() public view {} + function test_Push_UnderlyingIfBalanceLessThanOldBalance() private view {} - function test_Push_FeeIfBalanceLessThanOldBalance() public view {} + function test_Push_FeeIfBalanceLessThanOldBalance() private view {} - function test_Emit_LogCall() public view {} + function test_Emit_LogCall() private view {} } contract BPool_Unit_Unbind is Base { - function test_Revert_NotController() public view {} + function test_Revert_NotController() private view {} - function test_Revert_NotBound() public view {} + function test_Revert_NotBound() private view {} - function test_Revert_Finalized() public view {} + function test_Revert_Finalized() private view {} - function test_Revert_Reentrancy() public view {} + function test_Revert_Reentrancy() private view {} - function test_Set_TotalWeight() public view {} + function test_Set_TotalWeight() private view {} - function test_Set_TokenArray() public view {} + function test_Set_TokenArray() private view {} - function test_Set_Index() public view {} + function test_Set_Index() private view {} - function test_Unset_TokenArray() public view {} + function test_Unset_TokenArray() private view {} - function test_Unset_Record() public view {} + function test_Unset_Record() private view {} - function test_Push_UnderlyingBalance() public view {} + function test_Push_UnderlyingBalance() private view {} - function test_Push_UnderlyingFee() public view {} + function test_Push_UnderlyingFee() private view {} - function test_Emit_LogCall() public view {} + function test_Emit_LogCall() private view {} } contract BPool_Unit_Gulp is Base { - function test_Revert_NotBound() public view {} + function test_Revert_NotBound() private view {} - function test_Revert_Reentrancy() public view {} + function test_Revert_Reentrancy() private view {} - function test_Set_Balance() public view {} + function test_Set_Balance() private view {} - function test_Emit_LogCall() public view {} + function test_Emit_LogCall() private view {} } contract BPool_Unit_GetSpotPrice is Base { - function test_Revert_NotBoundTokenIn() public view {} + function test_Revert_NotBoundTokenIn() private view {} - function test_Revert_NotBoundTokenOut() public view {} + function test_Revert_NotBoundTokenOut() private view {} - function test_Returns_SpotPrice() public view {} + function test_Returns_SpotPrice() private view {} - function test_Revert_Reentrancy() public view {} + function test_Revert_Reentrancy() private view {} } contract BPool_Unit_GetSpotPriceSansFee is Base { - function test_Revert_NotBoundTokenIn() public view {} + function test_Revert_NotBoundTokenIn() private view {} - function test_Revert_NotBoundTokenOut() public view {} + function test_Revert_NotBoundTokenOut() private view {} - function test_Returns_SpotPrice() public view {} + function test_Returns_SpotPrice() private view {} - function test_Revert_Reentrancy() public view {} + function test_Revert_Reentrancy() private view {} } contract BPool_Unit_JoinPool is Base { @@ -285,27 +285,27 @@ contract BPool_Unit_JoinPool is Base { bPool.joinPool(_fuzz.poolAmountOut, maxAmountsIn); } - function test_Revert_NotFinalized() public view {} + function test_Revert_NotFinalized() private view {} - function test_Revert_MathApprox() public view {} + function test_Revert_MathApprox() private view {} - function test_Revert_TokenArrayMathApprox() public view {} + function test_Revert_TokenArrayMathApprox() private view {} - function test_Revert_TokenArrayLimitIn() public view {} + function test_Revert_TokenArrayLimitIn() private view {} - function test_Revert_Reentrancy() public view {} + function test_Revert_Reentrancy() private view {} - function test_Set_TokenArrayBalance() public view {} + function test_Set_TokenArrayBalance() private view {} - function test_Emit_TokenArrayLogJoin() public view {} + function test_Emit_TokenArrayLogJoin() private view {} - function test_Pull_TokenArrayTokenAmountIn() public view {} + function test_Pull_TokenArrayTokenAmountIn() private view {} - function test_Mint_PoolShare() public view {} + function test_Mint_PoolShare() private view {} - function test_Push_PoolShare() public view {} + function test_Push_PoolShare() private view {} - function test_Emit_LogCall() public view {} + function test_Emit_LogCall() private view {} modifier happyPath(FuzzScenario memory _fuzz) { _assumeHappyPath(_fuzz); @@ -364,211 +364,211 @@ contract BPool_Unit_JoinPool is Base { } contract BPool_Unit_ExitPool is Base { - function test_Revert_NotFinalized() public view {} + function test_Revert_NotFinalized() private view {} - function test_Revert_MathApprox() public view {} + function test_Revert_MathApprox() private view {} - function test_Pull_PoolShare() public view {} + function test_Pull_PoolShare() private view {} - function test_Push_PoolShare() public view {} + function test_Push_PoolShare() private view {} - function test_Burn_PoolShare() public view {} + function test_Burn_PoolShare() private view {} - function test_Revert_TokenArrayMathApprox() public view {} + function test_Revert_TokenArrayMathApprox() private view {} - function test_Revert_TokenArrayLimitOut() public view {} + function test_Revert_TokenArrayLimitOut() private view {} - function test_Revert_Reentrancy() public view {} + function test_Revert_Reentrancy() private view {} - function test_Set_TokenArrayBalance() public view {} + function test_Set_TokenArrayBalance() private view {} - function test_Emit_TokenArrayLogExit() public view {} + function test_Emit_TokenArrayLogExit() private view {} - function test_Push_TokenArrayTokenAmountOut() public view {} + function test_Push_TokenArrayTokenAmountOut() private view {} - function test_Emit_LogCall() public view {} + function test_Emit_LogCall() private view {} } contract BPool_Unit_SwapExactAmountIn is Base { - function test_Revert_NotBoundTokenIn() public view {} + function test_Revert_NotBoundTokenIn() private view {} - function test_Revert_NotBoundTokenOut() public view {} + function test_Revert_NotBoundTokenOut() private view {} - function test_Revert_NotPublic() public view {} + function test_Revert_NotPublic() private view {} - function test_Revert_MaxInRatio() public view {} + function test_Revert_MaxInRatio() private view {} - function test_Revert_BadLimitPrice() public view {} + function test_Revert_BadLimitPrice() private view {} - function test_Revert_LimitOut() public view {} + function test_Revert_LimitOut() private view {} - function test_Revert_Reentrancy() public view {} + function test_Revert_Reentrancy() private view {} - function test_Set_InRecord() public view {} + function test_Set_InRecord() private view {} - function test_Set_OutRecord() public view {} + function test_Set_OutRecord() private view {} - function test_Revert_MathApprox() public view {} + function test_Revert_MathApprox() private view {} - function test_Revert_LimitPrice() public view {} + function test_Revert_LimitPrice() private view {} - function test_Revert_MathApprox2() public view {} + function test_Revert_MathApprox2() private view {} - function test_Emit_LogSwap() public view {} + function test_Emit_LogSwap() private view {} - function test_Pull_TokenAmountIn() public view {} + function test_Pull_TokenAmountIn() private view {} - function test_Push_TokenAmountOut() public view {} + function test_Push_TokenAmountOut() private view {} - function test_Returns_AmountAndPrice() public view {} + function test_Returns_AmountAndPrice() private view {} - function test_Emit_LogCall() public view {} + function test_Emit_LogCall() private view {} } contract BPool_Unit_SwapExactAmountOut is Base { - function test_Revert_NotBoundTokenIn() public view {} + function test_Revert_NotBoundTokenIn() private view {} - function test_Revert_NotBoundTokenOut() public view {} + function test_Revert_NotBoundTokenOut() private view {} - function test_Revert_NotPublic() public view {} + function test_Revert_NotPublic() private view {} - function test_Revert_MaxOutRatio() public view {} + function test_Revert_MaxOutRatio() private view {} - function test_Revert_BadLimitPrice() public view {} + function test_Revert_BadLimitPrice() private view {} - function test_Revert_LimitIn() public view {} + function test_Revert_LimitIn() private view {} - function test_Revert_Reentrancy() public view {} + function test_Revert_Reentrancy() private view {} - function test_Set_InRecord() public view {} + function test_Set_InRecord() private view {} - function test_Set_OutRecord() public view {} + function test_Set_OutRecord() private view {} - function test_Revert_MathApprox() public view {} + function test_Revert_MathApprox() private view {} - function test_Revert_LimitPrice() public view {} + function test_Revert_LimitPrice() private view {} - function test_Revert_MathApprox2() public view {} + function test_Revert_MathApprox2() private view {} - function test_Emit_LogSwap() public view {} + function test_Emit_LogSwap() private view {} - function test_Pull_TokenAmountIn() public view {} + function test_Pull_TokenAmountIn() private view {} - function test_Push_TokenAmountOut() public view {} + function test_Push_TokenAmountOut() private view {} - function test_Returns_AmountAndPrice() public view {} + function test_Returns_AmountAndPrice() private view {} - function test_Emit_LogCall() public view {} + function test_Emit_LogCall() private view {} } contract BPool_Unit_JoinswapExternAmountIn is Base { - function test_Revert_NotFinalized() public view {} + function test_Revert_NotFinalized() private view {} - function test_Revert_NotBound() public view {} + function test_Revert_NotBound() private view {} - function test_Revert_MaxInRatio() public view {} + function test_Revert_MaxInRatio() private view {} - function test_Revert_LimitOut() public view {} + function test_Revert_LimitOut() private view {} - function test_Revert_Reentrancy() public view {} + function test_Revert_Reentrancy() private view {} - function test_Set_Balance() public view {} + function test_Set_Balance() private view {} - function test_Emit_LogJoin() public view {} + function test_Emit_LogJoin() private view {} - function test_Mint_PoolShare() public view {} + function test_Mint_PoolShare() private view {} - function test_Push_PoolShare() public view {} + function test_Push_PoolShare() private view {} - function test_Pull_Underlying() public view {} + function test_Pull_Underlying() private view {} - function test_Returns_PoolAmountOut() public view {} + function test_Returns_PoolAmountOut() private view {} - function test_Emit_LogCall() public view {} + function test_Emit_LogCall() private view {} } contract BPool_Unit_JoinswapExternAmountOut is Base { - function test_Revert_NotFinalized() public view {} + function test_Revert_NotFinalized() private view {} - function test_Revert_NotBound() public view {} + function test_Revert_NotBound() private view {} - function test_Revert_MaxApprox() public view {} + function test_Revert_MaxApprox() private view {} - function test_Revert_LimitIn() public view {} + function test_Revert_LimitIn() private view {} - function test_Revert_MaxInRatio() public view {} + function test_Revert_MaxInRatio() private view {} - function test_Revert_Reentrancy() public view {} + function test_Revert_Reentrancy() private view {} - function test_Set_Balance() public view {} + function test_Set_Balance() private view {} - function test_Emit_LogJoin() public view {} + function test_Emit_LogJoin() private view {} - function test_Mint_PoolShare() public view {} + function test_Mint_PoolShare() private view {} - function test_Push_PoolShare() public view {} + function test_Push_PoolShare() private view {} - function test_Pull_Underlying() public view {} + function test_Pull_Underlying() private view {} - function test_Returns_TokenAmountIn() public view {} + function test_Returns_TokenAmountIn() private view {} - function test_Emit_LogCall() public view {} + function test_Emit_LogCall() private view {} } contract BPool_Unit_ExitswapPoolAmountIn is Base { - function test_Revert_NotFinalized() public view {} + function test_Revert_NotFinalized() private view {} - function test_Revert_NotBound() public view {} + function test_Revert_NotBound() private view {} - function test_Revert_LimitOut() public view {} + function test_Revert_LimitOut() private view {} - function test_Revert_MaxOutRatio() public view {} + function test_Revert_MaxOutRatio() private view {} - function test_Revert_Reentrancy() public view {} + function test_Revert_Reentrancy() private view {} - function test_Set_Balance() public view {} + function test_Set_Balance() private view {} - function test_Emit_LogExit() public view {} + function test_Emit_LogExit() private view {} - function test_Pull_PoolShare() public view {} + function test_Pull_PoolShare() private view {} - function test_Burn_PoolShare() public view {} + function test_Burn_PoolShare() private view {} - function test_Push_PoolShare() public view {} + function test_Push_PoolShare() private view {} - function test_Push_Underlying() public view {} + function test_Push_Underlying() private view {} - function test_Returns_TokenAmountOut() public view {} + function test_Returns_TokenAmountOut() private view {} - function test_Emit_LogCall() public view {} + function test_Emit_LogCall() private view {} } contract BPool_Unit_ExitswapPoolAmountOut is Base { - function test_Revert_NotFinalized() public view {} + function test_Revert_NotFinalized() private view {} - function test_Revert_NotBound() public view {} + function test_Revert_NotBound() private view {} - function test_Revert_MaxOutRatio() public view {} + function test_Revert_MaxOutRatio() private view {} - function test_Revert_MathApprox() public view {} + function test_Revert_MathApprox() private view {} - function test_Revert_LimitIn() public view {} + function test_Revert_LimitIn() private view {} - function test_Revert_Reentrancy() public view {} + function test_Revert_Reentrancy() private view {} - function test_Set_Balance() public view {} + function test_Set_Balance() private view {} - function test_Emit_LogExit() public view {} + function test_Emit_LogExit() private view {} - function test_Pull_PoolShare() public view {} + function test_Pull_PoolShare() private view {} - function test_Burn_PoolShare() public view {} + function test_Burn_PoolShare() private view {} - function test_Push_PoolShare() public view {} + function test_Push_PoolShare() private view {} - function test_Push_Underlying() public view {} + function test_Push_Underlying() private view {} - function test_Returns_PoolAmountIn() public view {} + function test_Returns_PoolAmountIn() private view {} - function test_Emit_LogCall() public view {} + function test_Emit_LogCall() private view {} } From 589518b009f8f2e650cc15c772b4da4b3905857b Mon Sep 17 00:00:00 2001 From: 0xAustrian Date: Wed, 8 May 2024 17:03:22 -0300 Subject: [PATCH 14/18] test: small improvements --- test/unit/BPool.t.sol | 187 +++++++++++++++++++++--------------------- 1 file changed, 95 insertions(+), 92 deletions(-) diff --git a/test/unit/BPool.t.sol b/test/unit/BPool.t.sol index e5abcc94..808ad56e 100644 --- a/test/unit/BPool.t.sol +++ b/test/unit/BPool.t.sol @@ -7,14 +7,29 @@ import {IERC20} from 'contracts/BToken.sol'; import {Test} from 'forge-std/Test.sol'; import {LibString} from 'solmate/utils/LibString.sol'; -abstract contract Base is Test, BConst { +// TODO: remove once `private` keyword is removed in all test cases +/* solhint-disable */ + +abstract contract BasePoolTest is Test, BConst { using LibString for *; + struct FuzzScenario { + uint256 poolAmountOut; + uint256 initPoolSupply; + uint256[TOKENS_AMOUNT] balance; + } + uint256 public constant TOKENS_AMOUNT = 3; BPool public bPool; address[TOKENS_AMOUNT] public tokens; + modifier happyPath(FuzzScenario memory _fuzz) { + _assumeHappyPath(_fuzz); + _setValues(_fuzz); + _; + } + function setUp() public { bPool = new BPool(); @@ -23,37 +38,85 @@ abstract contract Base is Test, BConst { tokens[i] = makeAddr(i.toString()); } } + + function _setValues(FuzzScenario memory _fuzz) internal { + // Create mocks + for (uint256 i = 0; i < tokens.length; i++) { + vm.mockCall(tokens[i], abi.encodeWithSelector(IERC20(tokens[i]).transfer.selector), abi.encode(true)); + vm.mockCall(tokens[i], abi.encodeWithSelector(IERC20(tokens[i]).transferFrom.selector), abi.encode(true)); + } + + // Set tokens + bytes memory _arraySlot = abi.encode(9); + bytes32 _hashArraySlot = keccak256(_arraySlot); + vm.store(address(bPool), bytes32(_arraySlot), bytes32(tokens.length)); // write length + for (uint256 i = 0; i < tokens.length; i++) { + vm.store(address(bPool), bytes32(uint256(_hashArraySlot) + i), bytes32(abi.encode(tokens[i]))); // write token + } + + // Set balances + for (uint256 i = 0; i < tokens.length; i++) { + bytes32 _slot = keccak256(abi.encode(tokens[i], 10)); // mapping is found at slot 10 + vm.store(address(bPool), bytes32(uint256(_slot) + 0), bytes32(abi.encode(1))); // bound + vm.store(address(bPool), bytes32(uint256(_slot) + 3), bytes32(abi.encode(_fuzz.balance[i]))); // balance + } + + // Set public swap + vm.store( + address(bPool), + bytes32(uint256(6)), + bytes32(uint256(0x0000000000000000000000010000000000000000000000000000000000000000)) + ); + // Set finalize + vm.store(address(bPool), bytes32(uint256(8)), bytes32(uint256(1))); + // Set totalSupply + vm.store(address(bPool), bytes32(uint256(2)), bytes32(_fuzz.initPoolSupply)); + } + + function _assumeHappyPath(FuzzScenario memory _fuzz) internal view { + vm.assume(_fuzz.initPoolSupply >= INIT_POOL_SUPPLY); + vm.assume(_fuzz.poolAmountOut >= _fuzz.initPoolSupply); + vm.assume(_fuzz.poolAmountOut < type(uint256).max / BONE); + + uint256 _ratio = (_fuzz.poolAmountOut * BONE) / _fuzz.initPoolSupply; // bdiv uses '* BONE' + uint256 _maxTokenAmountIn = type(uint256).max / _ratio; + + for (uint256 i = 0; i < _fuzz.balance.length; i++) { + vm.assume(_fuzz.balance[i] >= MIN_BALANCE); + vm.assume(_fuzz.balance[i] <= _maxTokenAmountIn); // L272 + } + } } -contract BPool_Unit_Constructor is Base { +contract BPool_Unit_Constructor is BasePoolTest { function test_Deploy() private view {} } -contract BPool_Unit_IsPublicSwap is Base { +contract BPool_Unit_IsPublicSwap is BasePoolTest { function test_Returns_IsPublicSwap() private view {} } -contract BPool_Unit_IsFinalized is Base { +contract BPool_Unit_IsFinalized is BasePoolTest { function test_Returns_IsFinalized() private view {} } -contract BPool_Unit_IsBound is Base { +contract BPool_Unit_IsBound is BasePoolTest { function test_Returns_IsBound() private view {} function test_Returns_IsNotBound() private view {} } -contract BPool_Unit_GetNumTokens is Base { +contract BPool_Unit_GetNumTokens is BasePoolTest { function test_Returns_NumTokens() private view {} } -contract BPool_Unit_GetCurrentTokens is Base { +contract BPool_Unit_GetCurrentTokens is BasePoolTest { function test_Returns_CurrentTokens() private view {} function test_Revert_Reentrancy() private view {} } -contract BPool_Unit_GetFinalTokens is Base { +contract BPool_Unit_GetFinalTokens is BasePoolTest { function test_Returns_FinalTokens() private view {} function test_Revert_Reentrancy() private view {} @@ -61,7 +124,7 @@ contract BPool_Unit_GetFinalTokens is Base { function test_Revert_NotFinalized() private view {} } -contract BPool_Unit_GetDenormalizedWeight is Base { +contract BPool_Unit_GetDenormalizedWeight is BasePoolTest { function test_Returns_DenormalizedWeight() private view {} function test_Revert_Reentrancy() private view {} @@ -69,13 +132,13 @@ contract BPool_Unit_GetDenormalizedWeight is Base { function test_Revert_NotBound() private view {} } -contract BPool_Unit_GetTotalDenormalizedWeight is Base { +contract BPool_Unit_GetTotalDenormalizedWeight is BasePoolTest { function test_Returns_TotalDenormalizedWeight() private view {} function test_Revert_Reentrancy() private view {} } -contract BPool_Unit_GetNormalizedWeight is Base { +contract BPool_Unit_GetNormalizedWeight is BasePoolTest { function test_Returns_NormalizedWeight() private view {} function test_Revert_Reentrancy() private view {} @@ -83,7 +146,7 @@ contract BPool_Unit_GetNormalizedWeight is Base { function test_Revert_NotBound() private view {} } -contract BPool_Unit_GetBalance is Base { +contract BPool_Unit_GetBalance is BasePoolTest { function test_Returns_Balance() private view {} function test_Revert_Reentrancy() private view {} @@ -91,19 +154,19 @@ contract BPool_Unit_GetBalance is Base { function test_Revert_NotBound() private view {} } -contract BPool_Unit_GetSwapFee is Base { +contract BPool_Unit_GetSwapFee is BasePoolTest { function test_Returns_SwapFee() private view {} function test_Revert_Reentrancy() private view {} } -contract BPool_Unit_GetController is Base { +contract BPool_Unit_GetController is BasePoolTest { function test_Returns_Controller() private view {} function test_Revert_Reentrancy() private view {} } -contract BPool_Unit_SetSwapFee is Base { +contract BPool_Unit_SetSwapFee is BasePoolTest { function test_Revert_Finalized() private view {} function test_Revert_NotController() private view {} @@ -119,7 +182,7 @@ contract BPool_Unit_SetSwapFee is Base { function test_Emit_LogCall() private view {} } -contract BPool_Unit_SetController is Base { +contract BPool_Unit_SetController is BasePoolTest { function test_Revert_NotController() private view {} function test_Revert_Reentrancy() private view {} @@ -129,7 +192,7 @@ contract BPool_Unit_SetController is Base { function test_Emit_LogCall() private view {} } -contract BPool_Unit_SetPublicSwap is Base { +contract BPool_Unit_SetPublicSwap is BasePoolTest { function test_Revert_Finalized() private view {} function test_Revert_NotController() private view {} @@ -141,7 +204,7 @@ contract BPool_Unit_SetPublicSwap is Base { function test_Emit_LogCall() private view {} } -contract BPool_Unit_Finalize is Base { +contract BPool_Unit_Finalize is BasePoolTest { function test_Revert_NotController() private view {} function test_Revert_Finalized() private view {} @@ -161,7 +224,7 @@ contract BPool_Unit_Finalize is Base { function test_Emit_LogCall() private view {} } -contract BPool_Unit_Bind is Base { +contract BPool_Unit_Bind is BasePoolTest { function test_Revert_NotController() private view {} function test_Revert_IsBound() private view {} @@ -179,7 +242,7 @@ contract BPool_Unit_Bind is Base { function test_Call_Rebind() private view {} } -contract BPool_Unit_Rebind is Base { +contract BPool_Unit_Rebind is BasePoolTest { function test_Revert_NotController() private view {} function test_Revert_NotBound() private view {} @@ -213,7 +276,7 @@ contract BPool_Unit_Rebind is Base { function test_Emit_LogCall() private view {} } -contract BPool_Unit_Unbind is Base { +contract BPool_Unit_Unbind is BasePoolTest { function test_Revert_NotController() private view {} function test_Revert_NotBound() private view {} @@ -239,7 +302,7 @@ contract BPool_Unit_Unbind is Base { function test_Emit_LogCall() private view {} } -contract BPool_Unit_Gulp is Base { +contract BPool_Unit_Gulp is BasePoolTest { function test_Revert_NotBound() private view {} function test_Revert_Reentrancy() private view {} @@ -249,7 +312,7 @@ contract BPool_Unit_Gulp is Base { function test_Emit_LogCall() private view {} } -contract BPool_Unit_GetSpotPrice is Base { +contract BPool_Unit_GetSpotPrice is BasePoolTest { function test_Revert_NotBoundTokenIn() private view {} function test_Revert_NotBoundTokenOut() private view {} @@ -259,7 +322,7 @@ contract BPool_Unit_GetSpotPrice is Base { function test_Revert_Reentrancy() private view {} } -contract BPool_Unit_GetSpotPriceSansFee is Base { +contract BPool_Unit_GetSpotPriceSansFee is BasePoolTest { function test_Revert_NotBoundTokenIn() private view {} function test_Revert_NotBoundTokenOut() private view {} @@ -269,13 +332,7 @@ contract BPool_Unit_GetSpotPriceSansFee is Base { function test_Revert_Reentrancy() private view {} } -contract BPool_Unit_JoinPool is Base { - struct FuzzScenario { - uint256 poolAmountOut; - uint256 initPoolSupply; - uint256[TOKENS_AMOUNT] balance; - } - +contract BPool_Unit_JoinPool is BasePoolTest { function test_HappyPath(FuzzScenario memory _fuzz) public happyPath(_fuzz) { uint256[] memory maxAmountsIn = new uint256[](tokens.length); for (uint256 i = 0; i < tokens.length; i++) { @@ -306,63 +363,9 @@ contract BPool_Unit_JoinPool is Base { function test_Push_PoolShare() private view {} function test_Emit_LogCall() private view {} - - modifier happyPath(FuzzScenario memory _fuzz) { - _assumeHappyPath(_fuzz); - _setValues(_fuzz); - _; - } - - function _setValues(FuzzScenario memory _fuzz) internal { - // Create mocks - for (uint256 i = 0; i < tokens.length; i++) { - vm.mockCall(tokens[i], abi.encodeWithSelector(IERC20(tokens[i]).transfer.selector), abi.encode(true)); - vm.mockCall(tokens[i], abi.encodeWithSelector(IERC20(tokens[i]).transferFrom.selector), abi.encode(true)); - } - - // Set tokens - bytes memory _arraySlot = abi.encode(9); - bytes32 _hashArraySlot = keccak256(_arraySlot); - vm.store(address(bPool), bytes32(_arraySlot), bytes32(tokens.length)); // write length - for (uint256 i = 0; i < tokens.length; i++) { - vm.store(address(bPool), bytes32(uint256(_hashArraySlot) + i), bytes32(abi.encode(tokens[i]))); // write token - } - - // Set balances - for (uint256 i = 0; i < tokens.length; i++) { - bytes32 _slot = keccak256(abi.encode(tokens[i], 10)); // mapping is found at slot 10 - vm.store(address(bPool), bytes32(uint256(_slot) + 0), bytes32(abi.encode(1))); // bound - vm.store(address(bPool), bytes32(uint256(_slot) + 3), bytes32(abi.encode(_fuzz.balance[i]))); // balance - } - - // Set public swap - vm.store( - address(bPool), - bytes32(uint256(6)), - bytes32(uint256(0x0000000000000000000000010000000000000000000000000000000000000000)) - ); - // Set finalize - vm.store(address(bPool), bytes32(uint256(8)), bytes32(uint256(1))); - // Set totalSupply - vm.store(address(bPool), bytes32(uint256(2)), bytes32(_fuzz.initPoolSupply)); - } - - function _assumeHappyPath(FuzzScenario memory _fuzz) internal view { - vm.assume(_fuzz.initPoolSupply >= INIT_POOL_SUPPLY); - vm.assume(_fuzz.poolAmountOut >= _fuzz.initPoolSupply); - vm.assume(_fuzz.poolAmountOut < type(uint256).max / BONE); - - uint256 _ratio = (_fuzz.poolAmountOut * BONE) / _fuzz.initPoolSupply; // bdiv uses '* BONE' - uint256 _maxTokenAmountIn = type(uint256).max / _ratio; - - for (uint256 i = 0; i < _fuzz.balance.length; i++) { - vm.assume(_fuzz.balance[i] >= MIN_BALANCE); - vm.assume(_fuzz.balance[i] <= _maxTokenAmountIn); // L272 - } - } } -contract BPool_Unit_ExitPool is Base { +contract BPool_Unit_ExitPool is BasePoolTest { function test_Revert_NotFinalized() private view {} function test_Revert_MathApprox() private view {} @@ -388,7 +391,7 @@ contract BPool_Unit_ExitPool is Base { function test_Emit_LogCall() private view {} } -contract BPool_Unit_SwapExactAmountIn is Base { +contract BPool_Unit_SwapExactAmountIn is BasePoolTest { function test_Revert_NotBoundTokenIn() private view {} function test_Revert_NotBoundTokenOut() private view {} @@ -424,7 +427,7 @@ contract BPool_Unit_SwapExactAmountIn is Base { function test_Emit_LogCall() private view {} } -contract BPool_Unit_SwapExactAmountOut is Base { +contract BPool_Unit_SwapExactAmountOut is BasePoolTest { function test_Revert_NotBoundTokenIn() private view {} function test_Revert_NotBoundTokenOut() private view {} @@ -460,7 +463,7 @@ contract BPool_Unit_SwapExactAmountOut is Base { function test_Emit_LogCall() private view {} } -contract BPool_Unit_JoinswapExternAmountIn is Base { +contract BPool_Unit_JoinswapExternAmountIn is BasePoolTest { function test_Revert_NotFinalized() private view {} function test_Revert_NotBound() private view {} @@ -486,7 +489,7 @@ contract BPool_Unit_JoinswapExternAmountIn is Base { function test_Emit_LogCall() private view {} } -contract BPool_Unit_JoinswapExternAmountOut is Base { +contract BPool_Unit_JoinswapExternAmountOut is BasePoolTest { function test_Revert_NotFinalized() private view {} function test_Revert_NotBound() private view {} @@ -514,7 +517,7 @@ contract BPool_Unit_JoinswapExternAmountOut is Base { function test_Emit_LogCall() private view {} } -contract BPool_Unit_ExitswapPoolAmountIn is Base { +contract BPool_Unit_ExitswapPoolAmountIn is BasePoolTest { function test_Revert_NotFinalized() private view {} function test_Revert_NotBound() private view {} @@ -542,7 +545,7 @@ contract BPool_Unit_ExitswapPoolAmountIn is Base { function test_Emit_LogCall() private view {} } -contract BPool_Unit_ExitswapPoolAmountOut is Base { +contract BPool_Unit_ExitswapPoolAmountOut is BasePoolTest { function test_Revert_NotFinalized() private view {} function test_Revert_NotBound() private view {} From 7a4031db5008f70159c7cd57d0c4cbd78ab6abd9 Mon Sep 17 00:00:00 2001 From: 0xAustrian Date: Wed, 8 May 2024 17:03:53 -0300 Subject: [PATCH 15/18] fix: small compilation error --- test/unit/BPool.t.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/unit/BPool.t.sol b/test/unit/BPool.t.sol index 808ad56e..7a4329f9 100644 --- a/test/unit/BPool.t.sol +++ b/test/unit/BPool.t.sol @@ -13,14 +13,14 @@ import {LibString} from 'solmate/utils/LibString.sol'; abstract contract BasePoolTest is Test, BConst { using LibString for *; + uint256 public constant TOKENS_AMOUNT = 3; + struct FuzzScenario { uint256 poolAmountOut; uint256 initPoolSupply; uint256[TOKENS_AMOUNT] balance; } - uint256 public constant TOKENS_AMOUNT = 3; - BPool public bPool; address[TOKENS_AMOUNT] public tokens; From f7d4a744e870d59b24afba5d11efdf7612f96e74 Mon Sep 17 00:00:00 2001 From: 0xAustrian Date: Wed, 8 May 2024 21:33:37 -0300 Subject: [PATCH 16/18] test: create Utils library --- foundry.toml | 2 +- test/unit/BPool.t.sol | 28 ++++++++----------- test/unit/Utils.sol | 64 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 77 insertions(+), 17 deletions(-) create mode 100644 test/unit/Utils.sol diff --git a/foundry.toml b/foundry.toml index 8599d1c0..26cc6e77 100644 --- a/foundry.toml +++ b/foundry.toml @@ -26,7 +26,7 @@ src = 'src/interfaces/' [fuzz] runs = 1000 -max_test_rejects = 400000 +max_test_rejects = 500000 [rpc_endpoints] mainnet = "${MAINNET_RPC}" diff --git a/test/unit/BPool.t.sol b/test/unit/BPool.t.sol index 7a4329f9..5d55e6fd 100644 --- a/test/unit/BPool.t.sol +++ b/test/unit/BPool.t.sol @@ -6,11 +6,12 @@ import {BPool} from 'contracts/BPool.sol'; import {IERC20} from 'contracts/BToken.sol'; import {Test} from 'forge-std/Test.sol'; import {LibString} from 'solmate/utils/LibString.sol'; +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 { +abstract contract BasePoolTest is Test, BConst, Utils { using LibString for *; uint256 public constant TOKENS_AMOUNT = 3; @@ -47,33 +48,28 @@ abstract contract BasePoolTest is Test, BConst { } // Set tokens - bytes memory _arraySlot = abi.encode(9); - bytes32 _hashArraySlot = keccak256(_arraySlot); - vm.store(address(bPool), bytes32(_arraySlot), bytes32(tokens.length)); // write length + uint256 _arraySlotNumber = 9; + _writeArrayLengthToStorage(address(bPool), _arraySlotNumber, tokens.length); // write length for (uint256 i = 0; i < tokens.length; i++) { - vm.store(address(bPool), bytes32(uint256(_hashArraySlot) + i), bytes32(abi.encode(tokens[i]))); // write token + _writeAddressArrayItemToStorage(address(bPool), _arraySlotNumber, i, tokens[i]); // write token } // Set balances + uint256 _mappingSlotNumber = 10; for (uint256 i = 0; i < tokens.length; i++) { - bytes32 _slot = keccak256(abi.encode(tokens[i], 10)); // mapping is found at slot 10 - vm.store(address(bPool), bytes32(uint256(_slot) + 0), bytes32(abi.encode(1))); // bound - vm.store(address(bPool), bytes32(uint256(_slot) + 3), bytes32(abi.encode(_fuzz.balance[i]))); // balance + _writeStructPropertyAtAddressMapping(address(bPool), _mappingSlotNumber, tokens[i], 0, 1); // bound (1 == true) + _writeStructPropertyAtAddressMapping(address(bPool), _mappingSlotNumber, tokens[i], 3, _fuzz.balance[i]); // balance } // Set public swap - vm.store( - address(bPool), - bytes32(uint256(6)), - bytes32(uint256(0x0000000000000000000000010000000000000000000000000000000000000000)) - ); + _writeUintToStorage(address(bPool), 6, 0x0000000000000000000000010000000000000000000000000000000000000000); // Set finalize - vm.store(address(bPool), bytes32(uint256(8)), bytes32(uint256(1))); + _writeUintToStorage(address(bPool), 8, 1); // Set totalSupply - vm.store(address(bPool), bytes32(uint256(2)), bytes32(_fuzz.initPoolSupply)); + _writeUintToStorage(address(bPool), 2, _fuzz.initPoolSupply); } - function _assumeHappyPath(FuzzScenario memory _fuzz) internal view { + function _assumeHappyPath(FuzzScenario memory _fuzz) internal pure { vm.assume(_fuzz.initPoolSupply >= INIT_POOL_SUPPLY); vm.assume(_fuzz.poolAmountOut >= _fuzz.initPoolSupply); vm.assume(_fuzz.poolAmountOut < type(uint256).max / BONE); diff --git a/test/unit/Utils.sol b/test/unit/Utils.sol new file mode 100644 index 00000000..ba1c5e25 --- /dev/null +++ b/test/unit/Utils.sol @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.23; + +import {Test} from 'forge-std/Test.sol'; + +contract Utils is Test { + /** + * @dev Write a uint256 value to a storage slot. + * @param _target The address of the contract. + * @param _slotNumber The slot number to write to. + * @param _value The value to write. + */ + function _writeUintToStorage(address _target, uint256 _slotNumber, uint256 _value) internal { + vm.store(_target, bytes32(_slotNumber), bytes32(_value)); + } + + /** + * @dev Write the length of an array in storage. + * @dev This must be performed before writing any items to the array. + * @param _target The address of the contract. + * @param _arraySlotNumber The slot number of the array. + * @param _arrayLength The length of the array. + */ + function _writeArrayLengthToStorage(address _target, uint256 _arraySlotNumber, uint256 _arrayLength) internal { + _writeUintToStorage(_target, _arraySlotNumber, _arrayLength); + } + + /** + * @dev Write an address array item to a storage slot. + * @param _target The address of the contract. + * @param _arraySlotNumber The slot number of the array. + * @param _index The index of the item in the array. + * @param _value The address value to write. + */ + function _writeAddressArrayItemToStorage( + address _target, + uint256 _arraySlotNumber, + uint256 _index, + address _value + ) internal { + bytes memory _arraySlot = abi.encode(_arraySlotNumber); + bytes32 _hashArraySlot = keccak256(_arraySlot); + vm.store(_target, bytes32(uint256(_hashArraySlot) + _index), bytes32(abi.encode(_value))); + } + + /** + * @dev Write a struct property to a mapping in storage. + * @param _target The address of the contract. + * @param _mappingSlotNumber The slot number of the mapping. + * @param _mappingKey The address key of the mapping. + * @param _propertySlotNumber The slot number of the property in the struct. + * @param _value The value to write. + */ + function _writeStructPropertyAtAddressMapping( + address _target, + uint256 _mappingSlotNumber, + address _mappingKey, + uint256 _propertySlotNumber, + uint256 _value + ) internal { + bytes32 _slot = keccak256(abi.encode(_mappingKey, _mappingSlotNumber)); + _writeUintToStorage(_target, uint256(_slot) + _propertySlotNumber, _value); + } +} From 92d25919c2aa6115cb509639bcd15c44c6b34cc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wei=C3=9Fer=20Hase?= Date: Thu, 9 May 2024 13:02:30 +0200 Subject: [PATCH 17/18] fix: moving happy path to specific test contract --- test/unit/BPool.t.sol | 131 ++++++++++++++++++++++++++++-------------- 1 file changed, 87 insertions(+), 44 deletions(-) diff --git a/test/unit/BPool.t.sol b/test/unit/BPool.t.sol index 5d55e6fd..64038c18 100644 --- a/test/unit/BPool.t.sol +++ b/test/unit/BPool.t.sol @@ -15,22 +15,12 @@ abstract contract BasePoolTest is Test, BConst, Utils { using LibString for *; uint256 public constant TOKENS_AMOUNT = 3; - - struct FuzzScenario { - uint256 poolAmountOut; - uint256 initPoolSupply; - uint256[TOKENS_AMOUNT] balance; - } + uint256 internal constant _RECORD_MAPPING_SLOT_NUMBER = 10; + uint256 internal constant _TOKENS_ARRAY_SLOT_NUMBER = 9; BPool public bPool; address[TOKENS_AMOUNT] public tokens; - modifier happyPath(FuzzScenario memory _fuzz) { - _assumeHappyPath(_fuzz); - _setValues(_fuzz); - _; - } - function setUp() public { bPool = new BPool(); @@ -40,47 +30,50 @@ abstract contract BasePoolTest is Test, BConst, Utils { } } - function _setValues(FuzzScenario memory _fuzz) internal { - // Create mocks + function _tokensToMemory() internal view returns (address[] memory _tokens) { + _tokens = new address[](tokens.length); for (uint256 i = 0; i < tokens.length; i++) { - vm.mockCall(tokens[i], abi.encodeWithSelector(IERC20(tokens[i]).transfer.selector), abi.encode(true)); - vm.mockCall(tokens[i], abi.encodeWithSelector(IERC20(tokens[i]).transferFrom.selector), abi.encode(true)); + _tokens[i] = tokens[i]; } + } - // Set tokens - uint256 _arraySlotNumber = 9; - _writeArrayLengthToStorage(address(bPool), _arraySlotNumber, tokens.length); // write length - for (uint256 i = 0; i < tokens.length; i++) { - _writeAddressArrayItemToStorage(address(bPool), _arraySlotNumber, i, tokens[i]); // write token - } + function _mockTransfer(address _token) internal { + // TODO: add amount to transfer to check that it's called with the right amount + vm.mockCall(_token, abi.encodeWithSelector(IERC20(_token).transfer.selector), abi.encode(true)); + } - // Set balances - uint256 _mappingSlotNumber = 10; - for (uint256 i = 0; i < tokens.length; i++) { - _writeStructPropertyAtAddressMapping(address(bPool), _mappingSlotNumber, tokens[i], 0, 1); // bound (1 == true) - _writeStructPropertyAtAddressMapping(address(bPool), _mappingSlotNumber, tokens[i], 3, _fuzz.balance[i]); // balance + function _mockTransferFrom(address _token) internal { + // TODO: add from and amount to transfer to check that it's called with the right params + vm.mockCall(_token, abi.encodeWithSelector(IERC20(_token).transferFrom.selector), abi.encode(true)); + } + + function _setTokens(address[] memory _tokens) internal { + _writeArrayLengthToStorage(address(bPool), _TOKENS_ARRAY_SLOT_NUMBER, _tokens.length); // write length + for (uint256 i = 0; i < _tokens.length; i++) { + _writeAddressArrayItemToStorage(address(bPool), _TOKENS_ARRAY_SLOT_NUMBER, i, _tokens[i]); // write token } + } - // Set public swap - _writeUintToStorage(address(bPool), 6, 0x0000000000000000000000010000000000000000000000000000000000000000); - // Set finalize - _writeUintToStorage(address(bPool), 8, 1); - // Set totalSupply - _writeUintToStorage(address(bPool), 2, _fuzz.initPoolSupply); + function _setRecordBound(address _token) internal { + _writeStructPropertyAtAddressMapping(address(bPool), _RECORD_MAPPING_SLOT_NUMBER, _token, 0, 1); // bound (1 == true) } - function _assumeHappyPath(FuzzScenario memory _fuzz) internal pure { - vm.assume(_fuzz.initPoolSupply >= INIT_POOL_SUPPLY); - vm.assume(_fuzz.poolAmountOut >= _fuzz.initPoolSupply); - vm.assume(_fuzz.poolAmountOut < type(uint256).max / BONE); + function _setRecordBalance(address _token, uint256 _balance) internal { + _writeStructPropertyAtAddressMapping(address(bPool), _RECORD_MAPPING_SLOT_NUMBER, _token, 3, _balance); // balance + } - uint256 _ratio = (_fuzz.poolAmountOut * BONE) / _fuzz.initPoolSupply; // bdiv uses '* BONE' - uint256 _maxTokenAmountIn = type(uint256).max / _ratio; + function _setPublicSwap(bool _isPublicSwap) internal { + // TODO: make it depend on the bool value + _writeUintToStorage(address(bPool), 6, 0x0000000000000000000000010000000000000000000000000000000000000000); + } - for (uint256 i = 0; i < _fuzz.balance.length; i++) { - vm.assume(_fuzz.balance[i] >= MIN_BALANCE); - vm.assume(_fuzz.balance[i] <= _maxTokenAmountIn); // L272 - } + function _setFinalize(bool _isFinalized) internal { + // TODO: make it depend on the bool value + _writeUintToStorage(address(bPool), 8, 1); + } + + function _setTotalSupply(uint256 _totalSupply) internal { + _writeUintToStorage(address(bPool), 2, _totalSupply); } } @@ -329,7 +322,57 @@ contract BPool_Unit_GetSpotPriceSansFee is BasePoolTest { } contract BPool_Unit_JoinPool is BasePoolTest { - function test_HappyPath(FuzzScenario memory _fuzz) public happyPath(_fuzz) { + struct JoinPool_FuzzScenario { + uint256 poolAmountOut; + uint256 initPoolSupply; + uint256[TOKENS_AMOUNT] balance; + } + + function _setValues(JoinPool_FuzzScenario memory _fuzz) internal { + // Create mocks + for (uint256 i = 0; i < tokens.length; i++) { + _mockTransfer(tokens[i]); + _mockTransferFrom(tokens[i]); + } + + // Set tokens + _setTokens(_tokensToMemory()); + + // Set balances + for (uint256 i = 0; i < tokens.length; i++) { + _setRecordBound(tokens[i]); + _setRecordBalance(tokens[i], _fuzz.balance[i]); + } + + // Set public swap + _setPublicSwap(true); + // Set finalize + _setFinalize(true); + // Set totalSupply + _setTotalSupply(_fuzz.initPoolSupply); + } + + function _assumeHappyPath(JoinPool_FuzzScenario memory _fuzz) internal pure { + vm.assume(_fuzz.initPoolSupply >= INIT_POOL_SUPPLY); + vm.assume(_fuzz.poolAmountOut >= _fuzz.initPoolSupply); + vm.assume(_fuzz.poolAmountOut < type(uint256).max / BONE); + + uint256 _ratio = (_fuzz.poolAmountOut * BONE) / _fuzz.initPoolSupply; // bdiv uses '* BONE' + uint256 _maxTokenAmountIn = type(uint256).max / _ratio; + + for (uint256 i = 0; i < _fuzz.balance.length; i++) { + vm.assume(_fuzz.balance[i] >= MIN_BALANCE); + vm.assume(_fuzz.balance[i] <= _maxTokenAmountIn); // L272 + } + } + + modifier happyPath(JoinPool_FuzzScenario memory _fuzz) { + _assumeHappyPath(_fuzz); + _setValues(_fuzz); + _; + } + + function test_HappyPath(JoinPool_FuzzScenario memory _fuzz) public happyPath(_fuzz) { uint256[] memory maxAmountsIn = new uint256[](tokens.length); for (uint256 i = 0; i < tokens.length; i++) { maxAmountsIn[i] = type(uint256).max; From 202c317a58c312e404a2b72e77e05958f123daa2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wei=C3=9Fer=20Hase?= Date: Thu, 9 May 2024 13:06:55 +0200 Subject: [PATCH 18/18] fix: linter errors --- .solhint.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.solhint.json b/.solhint.json index 94f58b24..a8b002b7 100644 --- a/.solhint.json +++ b/.solhint.json @@ -13,6 +13,9 @@ "immutable-name-snakecase": "warn", "avoid-low-level-calls": "off", "no-console": "off", - "max-line-length": ["warn", 120] + "max-line-length": ["warn", 120], + "TODO": "REMOVE_TEMPORARY_LINTER_SETTINGS_BELOW", + "custom-errors": "warn", + "definition-name-capwords": "warn" } }