Skip to content

Commit

Permalink
fix: addressing comments from PR
Browse files Browse the repository at this point in the history
  • Loading branch information
wei3erHase committed Jul 16, 2024
1 parent e0de518 commit d0bba5c
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 57 deletions.
83 changes: 34 additions & 49 deletions test/integration/BCoWHelper.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -55,67 +54,54 @@ 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();
}

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);
Expand All @@ -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));
Expand All @@ -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
Expand All @@ -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;
Expand All @@ -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);

Expand All @@ -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);
}
}
26 changes: 24 additions & 2 deletions test/unit/BCoWHelper.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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]));
Expand All @@ -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 {
Expand Down Expand Up @@ -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);
}
}
14 changes: 8 additions & 6 deletions test/unit/BCoWHelper.tree
Original file line number Diff line number Diff line change
Expand Up @@ -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

0 comments on commit d0bba5c

Please sign in to comment.