diff --git a/test/integration/BCoWHelper.t.sol b/test/integration/BCoWHelper.t.sol index b256128a..cb83561c 100644 --- a/test/integration/BCoWHelper.t.sol +++ b/test/integration/BCoWHelper.t.sol @@ -40,11 +40,10 @@ contract ConstantProductHelperForkedTest is Test { IERC20 private constant DAI = IERC20(0x6B175474E89094C44Da98b954EedeAC495271d0F); IERC20 private constant WETH = IERC20(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); - // NOTE: minimum balance amount of any token to initialize a pool with - uint256 constant VALID_AMOUNT = 1e6; uint256 constant TEN_PERCENT = 0.1 ether; - // NOTE: 1 ETH = 1000 DAI + uint256 constant INITIAL_DAI_BALANCE = 1000 ether; + uint256 constant INITIAL_WETH_BALANCE = 1 ether; uint256 constant INITIAL_SPOT_PRICE = 0.001e18; function setUp() public { @@ -55,26 +54,27 @@ contract ConstantProductHelperForkedTest is Test { ammFactory = new BCoWFactory(address(settlement), bytes32('appData')); helper = new BCoWHelper(address(ammFactory)); - deal(address(DAI), lp, 2 * VALID_AMOUNT); - deal(address(WETH), lp, 2 * VALID_AMOUNT); + deal(address(DAI), lp, type(uint256).max, false); + deal(address(WETH), lp, type(uint256).max, false); vm.startPrank(lp); - weightedPool = ammFactory.newBPool(); basicPool = ammFactory.newBPool(); - - DAI.approve(address(weightedPool), type(uint256).max); - WETH.approve(address(weightedPool), type(uint256).max); - weightedPool.bind(address(DAI), VALID_AMOUNT, 8e18); // 80% weight - weightedPool.bind(address(WETH), VALID_AMOUNT, 2e18); // 20% weight + weightedPool = ammFactory.newBPool(); DAI.approve(address(basicPool), type(uint256).max); WETH.approve(address(basicPool), type(uint256).max); - basicPool.bind(address(DAI), VALID_AMOUNT, 4.2e18); // no weight - basicPool.bind(address(WETH), VALID_AMOUNT, 4.2e18); // no weight + basicPool.bind(address(DAI), INITIAL_DAI_BALANCE, 4.2e18); // no weight + basicPool.bind(address(WETH), INITIAL_WETH_BALANCE, 4.2e18); // no weight + + DAI.approve(address(weightedPool), type(uint256).max); + WETH.approve(address(weightedPool), type(uint256).max); + // NOTE: pool is 80-20 DAI-WETH, has 4xDAI balance than basic, same spot price + weightedPool.bind(address(DAI), 4 * INITIAL_DAI_BALANCE, 8e18); // 80% weight + weightedPool.bind(address(WETH), INITIAL_WETH_BALANCE, 2e18); // 20% weight // finalize - weightedPool.finalize(); basicPool.finalize(); + weightedPool.finalize(); vm.stopPrank(); } @@ -82,40 +82,26 @@ contract ConstantProductHelperForkedTest is Test { function testBasicOrder() public { IBCoWPool pool = IBCoWPool(address(basicPool)); - uint256 ammWethInitialBalance = 1 ether; - uint256 ammDaiInitialBalance = 1000 ether; - - deal(address(WETH), address(pool), ammWethInitialBalance); - deal(address(DAI), address(pool), ammDaiInitialBalance); - uint256 spotPrice = pool.getSpotPriceSansFee(address(WETH), address(DAI)); assertEq(spotPrice, INITIAL_SPOT_PRICE); - _executeHelperOrder(pool, ammWethInitialBalance, ammDaiInitialBalance); + _executeHelperOrder(pool); uint256 postSpotPrice = pool.getSpotPriceSansFee(address(WETH), address(DAI)); assertEq(postSpotPrice, 1_052_631_578_947_368); } // NOTE: this test checks that the order generated by the helper is valid - function testValidOrder(uint256 balanceToken0, uint256 balanceToken1, uint256 priceSkewness) public { + function testValidOrder(uint256 priceSkewness) public { IBCoWPool pool = IBCoWPool(address(basicPool)); - balanceToken0 = bound(balanceToken0, 1e18, 1e36); - balanceToken1 = bound(balanceToken1, 1e18, 1e36); + // skew the price by max 50% (more could result in reverts bc of max swap ratio) priceSkewness = bound(priceSkewness, 5000, 15_000); vm.assume(priceSkewness != 10_000); // avoids no-skewness revert - vm.mockCall( - address(DAI), abi.encodeWithSelector(IERC20.balanceOf.selector, address(pool)), abi.encode(balanceToken0) - ); - vm.mockCall( - address(WETH), abi.encodeWithSelector(IERC20.balanceOf.selector, address(pool)), abi.encode(balanceToken1) - ); - uint256[] memory prices = new uint256[](2); - prices[0] = balanceToken1; - prices[1] = balanceToken0 * priceSkewness / 10_000; + prices[0] = INITIAL_WETH_BALANCE; + prices[1] = INITIAL_DAI_BALANCE * priceSkewness / 10_000; // the helper generates the AMM order (GPv2Order.Data memory ammOrder,,,) = helper.order(address(pool), prices); @@ -138,7 +124,6 @@ contract ConstantProductHelperForkedTest is Test { uint256 ammDaiInitialBalance = 1000 ether; deal(address(WETH), address(pool), ammWethInitialBalance); - // NOTE: pool is 80-20 DAI-WETH, has 4xDAI balance than basic, same spot price deal(address(DAI), address(pool), 4 * ammDaiInitialBalance); uint256 spotPrice = pool.getSpotPriceSansFee(address(WETH), address(DAI)); @@ -153,19 +138,13 @@ contract ConstantProductHelperForkedTest is Test { // assertEq(postSpotPrice, 1_052_631_578_947_368); } - function addressVecToIerc20Vec(address[] memory addrVec) private pure returns (IERC20[] memory ierc20vec) { - assembly { - ierc20vec := addrVec - } - } - - function _executeHelperOrder(IBPool pool, uint256 ammWethInitialBalance, uint256 ammDaiInitialBalance) internal { - IERC20[] memory tokens = addressVecToIerc20Vec(helper.tokens(address(pool))); + function _executeHelperOrder(IBPool pool) internal { + address[] memory tokens = helper.tokens(address(pool)); uint256 daiIndex = 0; uint256 wethIndex = 1; assertEq(tokens.length, 2); - assertEq(address(tokens[daiIndex]), address(DAI)); - assertEq(address(tokens[wethIndex]), address(WETH)); + assertEq(tokens[daiIndex], address(DAI)); + assertEq(tokens[wethIndex], address(WETH)); // Prepare the price vector used in the execution of the settlement in // CoW Protocol. We skew the price by ~5% towards a cheaper WETH, so @@ -176,8 +155,8 @@ contract ConstantProductHelperForkedTest is Test { // if the first token is DAI and the second WETH then a price of 3000 // DAI per WETH means a price vector of [1, 3000] (if the decimals are // different, as in WETH/USDC, then the atom amount is what counts). - prices[daiIndex] = ammWethInitialBalance; - prices[wethIndex] = ammDaiInitialBalance * 95 / 100; + prices[daiIndex] = INITIAL_WETH_BALANCE; + prices[wethIndex] = INITIAL_DAI_BALANCE * 95 / 100; // The helper generates the AMM order GPv2Order.Data memory ammOrder; @@ -197,8 +176,8 @@ contract ConstantProductHelperForkedTest is Test { // Check that the amounts and price aren't unreasonable. We changed the // price by about 5%, so the amounts aren't expected to change // significantly more (say, about 2.5% of the original balance). - assertApproxEqRel(ammOrder.sellAmount, ammDaiInitialBalance * 25 / 1000, TEN_PERCENT); - assertApproxEqRel(ammOrder.buyAmount, ammWethInitialBalance * 25 / 1000, TEN_PERCENT); + assertApproxEqRel(ammOrder.sellAmount, INITIAL_DAI_BALANCE * 25 / 1000, TEN_PERCENT); + assertApproxEqRel(ammOrder.buyAmount, INITIAL_WETH_BALANCE * 25 / 1000, TEN_PERCENT); GPv2Trade.Data[] memory trades = new GPv2Trade.Data[](1); @@ -222,8 +201,14 @@ contract ConstantProductHelperForkedTest is Test { interactions[0][0] = preInteractions[0]; + // cast tokens array to IERC20 array + IERC20[] memory ierc20vec; + assembly { + ierc20vec := tokens + } + // finally, settle vm.prank(solver); - settlement.settle(tokens, prices, trades, interactions); + settlement.settle(ierc20vec, prices, trades, interactions); } } diff --git a/test/unit/BCoWHelper.t.sol b/test/unit/BCoWHelper.t.sol index 61d9885b..7eeffcc2 100644 --- a/test/unit/BCoWHelper.t.sol +++ b/test/unit/BCoWHelper.t.sol @@ -51,7 +51,7 @@ contract BCoWHelperTest is Test { pool.set__finalized(true); priceVector[0] = 1e18; - priceVector[1] = 1e18; + priceVector[1] = 1.05e18; vm.mockCall(tokens[0], abi.encodePacked(IERC20.balanceOf.selector), abi.encode(priceVector[0])); vm.mockCall(tokens[1], abi.encodePacked(IERC20.balanceOf.selector), abi.encode(priceVector[1])); @@ -71,9 +71,10 @@ contract BCoWHelperTest is Test { } function test_TokensRevertWhen_PoolIsNotRegisteredInFactory() external { + factory.mock_call_isBPool(address(pool), false); // it should revert vm.expectRevert(ICOWAMMPoolHelper.PoolDoesNotExist.selector); - helper.tokens(invalidPool); + helper.tokens(address(pool)); } function test_TokensRevertWhen_PoolHasLessThan2Tokens() external { @@ -153,4 +154,25 @@ contract BCoWHelperTest is Test { bytes memory validSig = abi.encodePacked(pool, abi.encode(order_)); assertEq(keccak256(validSig), keccak256(sig)); } + + function test_OrderGivenAPriceVector(uint256 priceSkewness, uint256 balanceToken0, uint256 balanceToken1) external { + // skew the price by max 50% (more could result in reverts bc of max swap ratio) + priceSkewness = bound(priceSkewness, 5000, 15_000); + vm.assume(priceSkewness != 10_000); // avoids no-skewness revert + + balanceToken0 = bound(balanceToken0, 1e18, 1e36); + balanceToken1 = bound(balanceToken1, 1e18, 1e36); + vm.mockCall(tokens[0], abi.encodePacked(IERC20.balanceOf.selector), abi.encode(balanceToken0)); + vm.mockCall(tokens[1], abi.encodePacked(IERC20.balanceOf.selector), abi.encode(balanceToken1)); + + uint256[] memory prices = new uint256[](2); + prices[0] = balanceToken1; + prices[1] = balanceToken0 * priceSkewness / 10_000; + + // it should return a valid pool order + (GPv2Order.Data memory ammOrder,,,) = helper.order(address(pool), prices); + + // this call should not revert + pool.verify(ammOrder); + } } diff --git a/test/unit/BCoWHelper.tree b/test/unit/BCoWHelper.tree index b4686c03..ebbb7387 100644 --- a/test/unit/BCoWHelper.tree +++ b/test/unit/BCoWHelper.tree @@ -18,9 +18,11 @@ BCoWHelperTest::tokens BCoWHelperTest::order ├── when the pool is not supported │ └── it should revert -└── when the pool is supported - ├── it should query the domain separator from the pool - ├── it should return a valid pool order - ├── it should return a commit pre-interaction - ├── it should return an empty post-interaction - └── it should return a valid signature +├── when the pool is supported +│ ├── it should query the domain separator from the pool +│ ├── it should return a valid pool order +│ ├── it should return a commit pre-interaction +│ ├── it should return an empty post-interaction +│ └── it should return a valid signature +└── given a price vector + └── it should return a valid pool order