Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

S2 upgr well remediations 2 #145

Merged
merged 2 commits into from
Sep 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 34 additions & 3 deletions src/functions/Stable2.sol
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ contract Stable2 is ProportionalLPToken2, IBeanstalkWellFunction {

// price threshold. more accurate pricing requires a lower threshold,
// at the cost of higher execution costs.
uint256 constant PRICE_THRESHOLD = 100; // 0.01%
uint256 constant PRICE_THRESHOLD = 10; // 0.001%

address immutable lookupTable;
uint256 immutable a;
Expand Down Expand Up @@ -86,17 +86,33 @@ contract Stable2 is ProportionalLPToken2, IBeanstalkWellFunction {
uint256 sumReserves = scaledReserves[0] + scaledReserves[1];
lpTokenSupply = sumReserves;
for (uint256 i = 0; i < 255; i++) {
bool stableOscillation;
uint256 dP = lpTokenSupply;
// If division by 0, this will be borked: only withdrawal will work. And that is good
dP = dP * lpTokenSupply / (scaledReserves[0] * N);
dP = dP * lpTokenSupply / (scaledReserves[1] * N);
uint256 prevReserves = lpTokenSupply;
lpTokenSupply = (Ann * sumReserves / A_PRECISION + (dP * N)) * lpTokenSupply
/ (((Ann - A_PRECISION) * lpTokenSupply / A_PRECISION) + ((N + 1) * dP));

// Equality with the precision of 1
// If the difference between the current lpTokenSupply and the previous lpTokenSupply is 2,
// Check that the oscillation is stable, and if so, return the average between the two.
if (lpTokenSupply > prevReserves) {
if (lpTokenSupply - prevReserves == 2) {
if (stableOscillation) {
return lpTokenSupply - 1;
}
stableOscillation = true;
}
if (lpTokenSupply - prevReserves <= 1) return lpTokenSupply;
} else {
if (prevReserves - lpTokenSupply == 2) {
if (stableOscillation) {
return lpTokenSupply + 1;
}
stableOscillation = true;
}
if (prevReserves - lpTokenSupply <= 1) return lpTokenSupply;
}
}
Expand Down Expand Up @@ -197,7 +213,7 @@ contract Stable2 is ProportionalLPToken2, IBeanstalkWellFunction {
uint256 parityReserve = lpTokenSupply / 2;

// update `scaledReserves` based on whether targetPrice is closer to low or high price:
if (pd.lutData.highPrice - pd.targetPrice > pd.targetPrice - pd.lutData.lowPrice) {
if (percentDiff(pd.lutData.highPrice, pd.targetPrice) > percentDiff(pd.lutData.lowPrice, pd.targetPrice)) {
// targetPrice is closer to lowPrice.
scaledReserves[i] = parityReserve * pd.lutData.lowPriceI / pd.lutData.precision;
scaledReserves[j] = parityReserve * pd.lutData.lowPriceJ / pd.lutData.precision;
Expand Down Expand Up @@ -281,7 +297,7 @@ contract Stable2 is ProportionalLPToken2, IBeanstalkWellFunction {

// update scaledReserve[j] such that calcRate(scaledReserves, i, j) = low/high Price,
// depending on which is closer to targetPrice.
if (pd.lutData.highPrice - pd.targetPrice > pd.targetPrice - pd.lutData.lowPrice) {
if (percentDiff(pd.lutData.highPrice, pd.targetPrice) > percentDiff(pd.lutData.lowPrice, pd.targetPrice)) {
// targetPrice is closer to lowPrice.
scaledReserves[j] = scaledReserves[i] * pd.lutData.lowPriceJ / pd.lutData.precision;

Expand Down Expand Up @@ -428,4 +444,19 @@ contract Stable2 is ProportionalLPToken2, IBeanstalkWellFunction {
+ pd.maxStepSize * (pd.currentPrice - pd.targetPrice) / (pd.lutData.highPrice - pd.lutData.lowPrice);
}
}

/**
* @notice Calculate the percentage difference between two numbers.
* @return The percentage difference as a fixed-point number with 18 decimals.
* @dev This function calculates the absolute percentage difference:
* |(a - b)| / ((a + b) / 2) * 100
* The result is scaled by 1e18 for precision.
*/
function percentDiff(uint256 _a, uint256 _b) internal pure returns (uint256) {
if (_a == _b) return 0;
uint256 difference = _a > _b ? _a - _b : _b - _a;
uint256 average = (_a + _b) / 2;
// Multiply by 100 * 1e18 to get percentage with 18 decimal places
return (difference * 100 * 1e18) / average;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ contract BeanstalkStable2LiquidityTest is TestHelper {
uint256 reserve0 = _f.calcReserveAtRatioLiquidity(reserves, 0, ratios, data);
uint256 reserve1 = _f.calcReserveAtRatioLiquidity(reserves, 1, ratios, data);

assertApproxEqRel(reserve0, 4.575771214546676444e18, 0.0001e18);
assertApproxEqRel(reserve0, 4.576236561359714812e18, 0.0001e18);
assertApproxEqRel(reserve1, 0.21852354514449462e18, 0.0001e18);
}

Expand Down
10 changes: 5 additions & 5 deletions test/beanstalk/BeanstalkStable2.calcReserveAtRatioSwap.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ contract BeanstalkStable2SwapTest is TestHelper {
uint256 reserve0 = _f.calcReserveAtRatioSwap(reserves, 0, ratios, data);
uint256 reserve1 = _f.calcReserveAtRatioSwap(reserves, 1, ratios, data);

assertEq(reserve0, 100.005058322101089709e18);
assertEq(reserve1, 100.005058322101089709e18);
assertEq(reserve0, 99.999921040536083478e18);
assertEq(reserve1, 99.999921040536083478e18);
}

function test_calcReserveAtRatioSwap_equal_diff() public view {
Expand All @@ -45,8 +45,8 @@ contract BeanstalkStable2SwapTest is TestHelper {
uint256 reserve0 = _f.calcReserveAtRatioSwap(reserves, 0, ratios, data);
uint256 reserve1 = _f.calcReserveAtRatioSwap(reserves, 1, ratios, data);

assertEq(reserve0, 73.517644476151580971e18);
assertEq(reserve1, 73.517644476151580971e18);
assertEq(reserve0, 73.513867858788351572e18);
assertEq(reserve1, 73.513867858788351572e18);
}

function test_calcReserveAtRatioSwap_diff_equal() public view {
Expand All @@ -61,7 +61,7 @@ contract BeanstalkStable2SwapTest is TestHelper {
uint256 reserve1 = _f.calcReserveAtRatioSwap(reserves, 1, ratios, data);

assertEq(reserve0, 180.644064978044534737e18); // 180.644064978044534737e18, 100e18
assertEq(reserve1, 39.475055811844664131e18); // 100e18, 39.475055811844664131e18
assertEq(reserve1, 39.474244037189430513e18); // 100e18, 39.475055811844664131e18
}

function test_calcReserveAtRatioSwap_diff_diff() public view {
Expand Down
Loading