@@ -50,34 +50,30 @@ library CurveLib {
50
50
int256 B;
51
51
uint256 C;
52
52
uint256 fourAC;
53
+
53
54
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
64
60
}
65
61
66
62
uint256 absB = uint256 (B >= 0 ? B : - B);
67
63
uint256 squaredB;
68
64
uint256 discriminant;
69
65
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
72
68
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
76
72
sqrt = (sqrt * sqrt < discriminant) ? sqrt + 1 : sqrt;
77
73
}
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
81
77
squaredB = Math.mulDiv (absB / scale, absB, scale, Math.Rounding.Ceil);
82
78
discriminant = squaredB + fourAC / (scale * scale);
83
79
sqrt = Math.sqrt (discriminant);
@@ -87,9 +83,11 @@ library CurveLib {
87
83
88
84
uint256 x;
89
85
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 ;
91
88
} 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 ;
93
91
}
94
92
95
93
if (x >= x0) {
@@ -116,4 +114,29 @@ library CurveLib {
116
114
scale = 1 ;
117
115
}
118
116
}
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