Merge pull request #52 from propeller-heads/cz/marginal-price-capability

Add marginal price capability and update integration tests
This commit is contained in:
Carlos Zanella
2024-07-19 11:03:41 +01:00
committed by GitHub
3 changed files with 45 additions and 23 deletions

View File

@@ -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;
}
}
}

View File

@@ -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.

View File

@@ -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;