Skip to content

Commit c4de78d

Browse files
committed
Use new derivation for f
1 parent 449beb4 commit c4de78d

File tree

3 files changed

+57
-2
lines changed

3 files changed

+57
-2
lines changed

docs/f.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
## Implementation of `f`
2+
3+
### Derivation
4+
5+
Formula 15 from the whitepaper:
6+
7+
y0 + (px / py) * (x0 - x) * (c + (1 - c) * (x0 / x))
8+
9+
Multiply second term by `x/x`:
10+
11+
y0 + (px / py) * (x0 - x) * ((c * x) + (1 - c) * x0) / x
12+
13+
`c` is scaled by `1e18`:
14+
15+
y0 + (px / py) * (x0 - x) * ((c * x) + (1e18 - c) * x0) / (x * 1e18)
16+
17+
Re-order division by `py`:
18+
19+
y0 + px * (x0 - x) * ((c * x) + (1e18 - c) * x0) / (x * 1e18) / py
20+
21+
Use `mulDiv` to avoid intermediate overflow:
22+
23+
y0 + Math.mulDiv(px * (x0 - x), c * x + (1e18 - c) * x0, x * 1e18) / py
24+
25+
Round up for both divisions (operation is distributive):
26+
27+
y0 + (Math.mulDiv(px * (x0 - x), c * x + (1e18 - c) * x0, x * 1e18, Math.Rounding.Ceil) + (py-1)) / py
28+
29+
### Boundary Analysis
30+
31+
Pre-conditions: x <= x0, 1 <= {px,py} <= 1e36, {x0,y0} <= type(uint112).max, c <= 1e18
32+
33+
None of the computations for the arguments to `mulDiv` can overflow:
34+
35+
* Arg 1: `px * (x0 - x)`
36+
* Upper-bound: `1e36*(2**112 - 1) =~ 232 bits`
37+
* Arg 2: `c * x + (1e18 - c) * x0`
38+
* Upper-bound: `1e18*(2**112 - 1)*2 =~ 173 bits`
39+
* Arg 3: `x * 1e18`
40+
* Upper-bound: `1e18*(2**112 - 1) =~ 172 bits`
41+
42+
If amounts/prices are large, and we travel too far down the curve, then `mulDiv` (or the subsequent `y0` addition) could overflow because its output value cannot be represented as a `uint256`. However, these output values would never be valid anyway, because they exceed `type(uint112).max`.

src/EulerSwap.sol

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {IUniswapV2Callee} from "./interfaces/IUniswapV2Callee.sol";
77
import {IEulerSwap} from "./interfaces/IEulerSwap.sol";
88
import {IAllowanceTransfer} from "permit2/src/interfaces/IAllowanceTransfer.sol";
99
import {EVCUtil} from "evc/utils/EVCUtil.sol";
10+
import {Math} from "openzeppelin-contracts/utils/math/Math.sol";
1011

1112
contract EulerSwap is IEulerSwap, EVCUtil {
1213
bytes32 public constant curve = keccak256("EulerSwap v1");
@@ -236,7 +237,8 @@ contract EulerSwap is IEulerSwap, EVCUtil {
236237
}
237238

238239
/// @dev EulerSwap curve definition
239-
function f(uint256 xt, uint256 px, uint256 py, uint256 x0, uint256 y0, uint256 c) internal pure returns (uint256) {
240-
return y0 + px * 1e18 / py * (c * (2 * x0 - xt) / 1e18 + (1e18 - c) * x0 / 1e18 * x0 / xt - x0) / 1e18;
240+
/// Pre-conditions: x <= x0, 1 <= {px,py} <= 1e36, {x0,y0} <= type(uint112).max, c <= 1e18
241+
function f(uint256 x, uint256 px, uint256 py, uint256 x0, uint256 y0, uint256 c) public pure returns (uint256) {
242+
return y0 + (Math.mulDiv(px * (x0 - x), c * x + (1e18 - c) * x0, x * 1e18, Math.Rounding.Ceil) + (py - 1)) / py;
241243
}
242244
}

test/EulerSwapTest.t.sol

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,4 +175,15 @@ contract EulerSwapTest is EulerSwapTestBase {
175175
assertGe(getHolderNAV(), origNAV);
176176
}
177177
}
178+
179+
function test_fFuncOverflow(uint256 xt, uint256 px, uint256 py, uint256 x0, uint256 y0, uint256 c) public view {
180+
x0 = bound(x0, 1, type(uint112).max);
181+
y0 = bound(y0, 0, type(uint112).max);
182+
xt = bound(xt, 1 + x0 / 1e6, x0); // from 1 millionth of the reserves left up
183+
px = bound(px, 1, 1e36);
184+
py = bound(py, 1, 1e36);
185+
c = bound(c, 1, 1e18);
186+
187+
eulerSwap.f(xt, px, py, x0, y0, c);
188+
}
178189
}

0 commit comments

Comments
 (0)