diff --git a/evm/src/balancer-v2/BalancerV2SwapAdapter.sol b/evm/src/balancer-v2/BalancerV2SwapAdapter.sol index 9aacef9..846da0d 100644 --- a/evm/src/balancer-v2/BalancerV2SwapAdapter.sol +++ b/evm/src/balancer-v2/BalancerV2SwapAdapter.sol @@ -9,7 +9,7 @@ import { // Maximum Swap In/Out Ratio - 0.3 // https://balancer.gitbook.io/balancer/core-concepts/protocol/limitations#v2-limits -uint256 constant RESERVE_LIMIT_FACTOR = 4; +uint256 constant RESERVE_LIMIT_FACTOR = 3; uint256 constant SWAP_DEADLINE_SEC = 1000; contract BalancerV2SwapAdapter is ISwapAdapter { @@ -174,10 +174,10 @@ contract BalancerV2SwapAdapter is ISwapAdapter { for (uint256 i = 0; i < tokens.length; i++) { if (tokens[i] == sellToken) { - limits[0] = balances[i] / RESERVE_LIMIT_FACTOR; + limits[0] = balances[i] * RESERVE_LIMIT_FACTOR / 10; } if (tokens[i] == buyToken) { - limits[1] = balances[i] / RESERVE_LIMIT_FACTOR; + limits[1] = balances[i] * RESERVE_LIMIT_FACTOR / 10; } } } diff --git a/evm/src/interfaces/ISwapAdapterTypes.sol b/evm/src/interfaces/ISwapAdapterTypes.sol index cb9e5e5..323ac0a 100644 --- a/evm/src/interfaces/ISwapAdapterTypes.sol +++ b/evm/src/interfaces/ISwapAdapterTypes.sol @@ -33,7 +33,10 @@ interface ISwapAdapterTypes { ScaledPrices, // Indicates that if we try to go over the sell limits, the pool will // revert (optional) - HardLimits + HardLimits, + // Indicates whether the pool's price function can be called with + // amountIn=0 to return the current price (optional) + MarginalPrice } /// @dev Representation used for rational numbers such as prices. diff --git a/evm/test/AdapterTest.sol b/evm/test/AdapterTest.sol index cb3b26a..643064f 100644 --- a/evm/test/AdapterTest.sol +++ b/evm/test/AdapterTest.sol @@ -11,7 +11,7 @@ contract AdapterTest is Test, ISwapAdapterTypes { using FractionMath for Fraction; uint256 constant pricePrecision = 10e24; - string[] public stringPctgs = ["0%", "0.01%", "50%", "100%"]; + string[] public stringPctgs = ["0%", "0.1%", "50%", "100%"]; // @notice Test the behavior of a swap adapter for a list of pools // @dev Computes limits, prices, and swaps on the pools on both directions @@ -29,6 +29,7 @@ contract AdapterTest is Test, ISwapAdapterTypes { for (uint256 i = 0; i < poolIds.length; i++) { address[] memory tokens = adapter.getTokens(poolIds[i]); IERC20(tokens[0]).approve(address(adapter), type(uint256).max); + IERC20(tokens[1]).approve(address(adapter), type(uint256).max); testPricesForPair( adapter, poolIds[i], tokens[0], tokens[1], hasPriceImpact @@ -59,7 +60,13 @@ contract AdapterTest is Test, ISwapAdapterTypes { tokenOut, sellLimit ); - uint256[] memory amounts = calculateTestAmounts(sellLimit); + + bool hasMarginalPrices = hasCapability( + adapter.getCapabilities(poolId, tokenIn, tokenOut), + Capability.MarginalPrice + ); + uint256[] memory amounts = + calculateTestAmounts(sellLimit, hasMarginalPrices); Fraction[] memory prices = adapter.price(poolId, tokenIn, tokenOut, amounts); assertGt( @@ -78,17 +85,17 @@ contract AdapterTest is Test, ISwapAdapterTypes { assertGt(prices[0].denominator, 0, "Denominator shouldn't be 0"); Trade memory trade; - deal(tokenIn, address(this), 2 * amounts[amounts.length - 1]); + deal(tokenIn, address(this), 5 * amounts[amounts.length - 1]); + + uint256 initialState = vm.snapshot(); for (uint256 j = 1; j < amounts.length; j++) { console2.log( - "TEST: Testing behavior for price at %s of limit: %d", + "TEST: Testing behavior for price at %s of limit.", stringPctgs[j], amounts[j] ); uint256 priceAtAmount = fractionToInt(prices[j]); - assertGt(prices[j].numerator, 0, "Nominator shouldn't be 0"); - assertGt(prices[j].denominator, 0, "Denominator shouldn't be 0"); console2.log("TEST: Swapping %d of %s", amounts[j], tokenIn); trade = adapter.swap( @@ -97,12 +104,12 @@ contract AdapterTest is Test, ISwapAdapterTypes { uint256 executedPrice = trade.calculatedAmount * pricePrecision / amounts[j]; uint256 priceAfterSwap = fractionToInt(trade.price); - console2.log("TEST: Pool price: %d", priceAtAmount); - console2.log("TEST: Executed price: %d", executedPrice); - console2.log("TEST: Price after swap: %d", priceAfterSwap); + console2.log("TEST: - Pool price: %d", priceAtAmount); + console2.log("TEST: - Executed price: %d", executedPrice); + console2.log("TEST: - Price after swap: %d", priceAfterSwap); if (hasPriceImpact) { - assertGt( + assertGe( priceAtAmount, executedPrice, "Price should be greated than executed price." @@ -121,19 +128,21 @@ contract AdapterTest is Test, ISwapAdapterTypes { assertGe( priceAtAmount, executedPrice, - "Price should be greated than executed price." + "Price should be greater or equal to executed price." ); assertGe( executedPrice, priceAfterSwap, - "Executed price should be greater than price after swap." + "Executed price should be or equal to price after swap." ); assertGe( priceAtAmount, priceAfterSwap, - "Price should be greated than price after swap." + "Price should be or equal to price after swap." ); } + + vm.revertTo(initialState); } uint256 amountAboveLimit = sellLimit * 105 / 100; @@ -151,6 +160,8 @@ contract AdapterTest is Test, ISwapAdapterTypes { adapter, poolId, tokenIn, tokenOut, amountAboveLimit ); } + + console2.log("TEST: All tests passed."); } function testRevertAboveLimit( @@ -160,12 +171,17 @@ contract AdapterTest is Test, ISwapAdapterTypes { address tokenOut, uint256 amountAboveLimit ) internal { - console2.log("TEST: Testing revert behavior above the sell limit"); + console2.log( + "TEST: Testing revert behavior above the sell limit: %d", + amountAboveLimit + ); uint256[] memory aboveLimitArray = new uint256[](1); aboveLimitArray[0] = amountAboveLimit; try adapter.price(poolId, tokenIn, tokenOut, aboveLimitArray) { - revert("Pool shouldn't be fetch prices above the sell limit"); + revert( + "Pool shouldn't be able to fetch prices above the sell limit" + ); } catch Error(string memory s) { console2.log( "TEST: Expected error when fetching price above limit: %s", s @@ -189,7 +205,10 @@ contract AdapterTest is Test, ISwapAdapterTypes { address tokenOut, uint256 amountAboveLimit ) internal { - console2.log("TEST: Testing operations above the sell limit"); + console2.log( + "TEST: Testing operations above the sell limit: %d", + amountAboveLimit + ); uint256[] memory aboveLimitArray = new uint256[](1); aboveLimitArray[0] = amountAboveLimit; @@ -199,14 +218,14 @@ contract AdapterTest is Test, ISwapAdapterTypes { ); } - function calculateTestAmounts(uint256 limit) + function calculateTestAmounts(uint256 limit, bool hasMarginalPrices) internal pure returns (uint256[] memory) { uint256[] memory amounts = new uint256[](4); - amounts[0] = 0; - amounts[1] = limit / 10000; + amounts[0] = hasMarginalPrices ? 0 : limit / 10000; + amounts[1] = limit / 1000; amounts[2] = limit / 2; amounts[3] = limit; return amounts;