Skip to content

Commit debf2b7

Browse files
committed
fix: scale issue in fInverse
1 parent 5097bb6 commit debf2b7

File tree

1 file changed

+44
-21
lines changed

1 file changed

+44
-21
lines changed

src/libraries/CurveLib.sol

Lines changed: 44 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -50,34 +50,30 @@ library CurveLib {
5050
int256 B;
5151
uint256 C;
5252
uint256 fourAC;
53+
5354
unchecked {
54-
B = int256((py * (y - y0) + (px - 1)) / px) - (2 * int256(c) - int256(1e18)) * int256(x0) / 1e18;
55-
if (x0 >= 1e18) {
56-
// if x0 >= 1, scale as normal
57-
C = Math.mulDiv((1e18 - c), x0 * x0, 1e36, Math.Rounding.Ceil);
58-
fourAC = 4 * c * C;
59-
} else {
60-
// if x0 < 1, then numbers get very small, so decrease scale to 1e18 to increase precision later
61-
C = ((1e18 - c) * x0 * x0 + (1e18 - 1)) / 1e18;
62-
fourAC = Math.mulDiv(4 * c, C, 1e18, Math.Rounding.Ceil);
63-
}
55+
int256 term1 = int256(Math.mulDiv(py * 1e18, y - y0, px, Math.Rounding.Ceil)); // scale: 1e36
56+
int256 term2 = (2 * int256(c) - int256(1e18)) * int256(x0); // scale: 1e36
57+
B = (term1 - term2) / int256(1e18); // scale: 1e18
58+
C = Math.mulDiv((1e18 - c), x0 * x0, 1e18, Math.Rounding.Ceil); // scale: 1e36
59+
fourAC = Math.mulDiv(4 * c, C, 1e18, Math.Rounding.Ceil); // scale: 1e36
6460
}
6561

6662
uint256 absB = uint256(B >= 0 ? B : -B);
6763
uint256 squaredB;
6864
uint256 discriminant;
6965
uint256 sqrt;
70-
if (absB < 1e36) {
71-
// safe to use naive squaring
66+
if (absB < 1e36) {
67+
// B^2 can be calculated directly at 1e18 scale without overflowing
7268
unchecked {
73-
squaredB = absB * absB;
74-
discriminant = squaredB + fourAC; // keep in 1e36 scale for increased precision ahead of sqrt
75-
sqrt = Math.sqrt(discriminant); // drop back to 1e18 scale
69+
squaredB = absB * absB; // scale: 1e36
70+
discriminant = squaredB + fourAC; // scale: 1e36
71+
sqrt = Math.sqrt(discriminant); // // scale: 1e18
7672
sqrt = (sqrt * sqrt < discriminant) ? sqrt + 1 : sqrt;
7773
}
78-
} else {
79-
// use scaled, overflow-safe path
80-
uint256 scale = computeScale(absB);
74+
} else {
75+
// B^2 cannot be calculated directly at 1e18 scale without overflowing
76+
uint256 scale = computeScale(absB); // calculate the scaling factor such that B^2 can be calculated without overflowing
8177
squaredB = Math.mulDiv(absB / scale, absB, scale, Math.Rounding.Ceil);
8278
discriminant = squaredB + fourAC / (scale * scale);
8379
sqrt = Math.sqrt(discriminant);
@@ -87,9 +83,11 @@ library CurveLib {
8783

8884
uint256 x;
8985
if (B <= 0) {
90-
x = Math.mulDiv(absB + sqrt, 1e18, 2 * c, Math.Rounding.Ceil) + 3;
86+
// use the regular quadratic formula solution (-b + sqrt(b^2 - 4ac)) / 2a
87+
x = Math.mulDiv(absB + sqrt, 1e18, 2 * c, Math.Rounding.Ceil) + 1;
9188
} else {
92-
x = Math.mulDiv(2 * C, 1e18, absB + sqrt, Math.Rounding.Ceil) + 3;
89+
// use the "citardauq" quadratic formula solution 2c / (-b + sqrt(b^2 - 4ac))
90+
x = (2 * C + (absB + sqrt - 1)) / (absB + sqrt) + 1;
9391
}
9492

9593
if (x >= x0) {
@@ -116,4 +114,29 @@ library CurveLib {
116114
scale = 1;
117115
}
118116
}
119-
}
117+
118+
/// @dev Less efficient method to compute fInverse. Useful for testing.
119+
function binarySearch(IEulerSwap.Params memory p, uint256 newReserve1, uint256 xMin, uint256 xMax)
120+
internal
121+
pure
122+
returns (uint256)
123+
{
124+
if (xMin < 1) {
125+
xMin = 1;
126+
}
127+
while (xMin < xMax) {
128+
uint256 xMid = (xMin + xMax) / 2;
129+
uint256 fxMid = f(xMid, p.priceX, p.priceY, p.equilibriumReserve0, p.equilibriumReserve1, p.concentrationX);
130+
if (newReserve1 >= fxMid) {
131+
xMax = xMid;
132+
} else {
133+
xMin = xMid + 1;
134+
}
135+
}
136+
if (newReserve1 < f(xMin, p.priceX, p.priceY, p.equilibriumReserve0, p.equilibriumReserve1, p.concentrationX)) {
137+
xMin += 1;
138+
}
139+
return xMin;
140+
}
141+
142+
}

0 commit comments

Comments
 (0)