From 772566c590657eea040156700dc3a497d4c8cfc7 Mon Sep 17 00:00:00 2001 From: czanella Date: Thu, 18 Jul 2024 16:12:50 +0100 Subject: [PATCH] Add marginal price capability and update integration tests Balancer now passes the test Some misc aesthetics --- evm/src/balancer-v2/BalancerV2SwapAdapter.sol | 3 +- evm/src/interfaces/ISwapAdapterTypes.sol | 6 ++- evm/test/AdapterTest.sol | 43 +++++++++++-------- 3 files changed, 32 insertions(+), 20 deletions(-) diff --git a/evm/src/balancer-v2/BalancerV2SwapAdapter.sol b/evm/src/balancer-v2/BalancerV2SwapAdapter.sol index 9aacef9..e93702b 100644 --- a/evm/src/balancer-v2/BalancerV2SwapAdapter.sol +++ b/evm/src/balancer-v2/BalancerV2SwapAdapter.sol @@ -188,11 +188,10 @@ contract BalancerV2SwapAdapter is ISwapAdapter { override returns (Capability[] memory capabilities) { - capabilities = new Capability[](4); + capabilities = new Capability[](3); capabilities[0] = Capability.SellOrder; capabilities[1] = Capability.BuyOrder; capabilities[2] = Capability.PriceFunction; - capabilities[3] = Capability.HardLimits; } function getTokens(bytes32 poolId) diff --git a/evm/src/interfaces/ISwapAdapterTypes.sol b/evm/src/interfaces/ISwapAdapterTypes.sol index cb9e5e5..e033df6 100644 --- a/evm/src/interfaces/ISwapAdapterTypes.sol +++ b/evm/src/interfaces/ISwapAdapterTypes.sol @@ -33,7 +33,11 @@ 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..1ca474d 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,15 @@ 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]); 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 +102,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,17 +126,17 @@ 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." ); } } @@ -151,6 +156,8 @@ contract AdapterTest is Test, ISwapAdapterTypes { adapter, poolId, tokenIn, tokenOut, amountAboveLimit ); } + + console2.log("TEST: All tests passed."); } function testRevertAboveLimit( @@ -165,7 +172,9 @@ contract AdapterTest is Test, ISwapAdapterTypes { 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 @@ -199,14 +208,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;