Skip to content

Commit b4e4b83

Browse files
Merge branch 'master' into doc-improvements
Signed-off-by: Haythem Sellami <[email protected]>
2 parents a789a30 + bbfa037 commit b4e4b83

14 files changed

+101
-93
lines changed

SECURITY.md

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,25 @@
1-
# Security policy
1+
# Euler Security Policy
22

3-
## Reporting a vulnerability
3+
## Vulnerability Disclosure and Bug Bounty
44

5-
If you discover a security vulnerability, please report it responsibly. Do not publicly disclose the issue until your report has been addressed. Resources for reporting:
5+
Security is a top priority at Euler, and we engage in regular security reviews and have an active bug bounty program to ensure the integrity of our systems.
66

7-
- **Email:** [email protected]
8-
- **Further reading:** https://docs.euler.finance/security/overview
7+
To report a vulnerability, **please submit it through our bug bounty program**:
8+
[Euler Bug Bounty](https://euler.finance/bug-bounty)
9+
10+
**Reports sent via email will not be accepted.** Email should only be used for general security inquiries.
11+
12+
## Security Team Contact Details
13+
14+
For security-related questions or inquiries (not vulnerability reports), you can contact us via:
15+
- **Email**: [[email protected]](mailto:[email protected])
16+
- **PGP Encryption**: [Euler Public Key](https://euler.finance/.well-known/public-key.asc)
17+
18+
## Previous Security Reviews
19+
20+
Euler undergoes regular security audits. You can find details of previous security reviews here:
21+
[Euler Security Reviews](https://docs.euler.finance/security/security-reviews)
22+
23+
## Preferred Languages
24+
25+
We accept security-related inquiries in **English (en)**

TODO

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,18 @@
1-
! Don't make quotes that would cause a swap to fail if supply/borrow caps exceeded
2-
* Better revert messages when a swap fails due to maglev debt-limit/vault utilisation/etc
3-
* currently it's an arithmetic underflow
1+
* Better revert messages when a swap fails due to debt-limit/vault utilisation/etc
2+
* currently they are errors thrown by the vaults or arithmetic underflows
43

54

65
TESTING
76

87
* when exchange rate in vaults != 1
9-
* uniswap callback, flash swaps
10-
* hitting reserve/utilisation limits
11-
* AssetsOutOfOrderOrEqual
128

139

1410
MISC
1511

1612
? A really small swap could fail because deposit() results in 0 shares, which causes EVK to revert. Call convertToShares() first? Seems like overkill...
17-
? permit2 instead of regular approval: measure gas savings
1813
* Improve the efficiency of on-chain quoting
1914
* Probably necessary for supporting non-zero slippage swaps
20-
* Use unchecked math in verify() (needs careful boundary analysis)
21-
* Closed-form quoting solutions
15+
* Use fInverse() Closed-form quoting solutions
2216
* "Range hints" for the binary search
2317

2418

@@ -34,7 +28,3 @@ IDEAS
3428
* Could current reserves be calculated dynamically based on balances/debts/debt limits?
3529
* I guess you would lose a chunk of interest to arbitrage
3630
* Donation attacks?
37-
* What can we do to make this easily integrated with aggregators/MEV bots/etc?
38-
* How to handle a discovery/tracking of the different Maglev instances?
39-
? Factory? Registry? Maybe a fake factory that reads the actually installed operators from a set of addresses?
40-
? Transparent proxy so a Maglev address can stay constant

docs/interfaces.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -46,13 +46,13 @@ The `IEulerSwap` interface defines the core functionality for executing token sw
4646

4747
- **description**: Returns the address of the account managing EulerSwap.
4848

49-
#### `initialReserve0() external view returns (uint112);`
49+
#### `equilibriumReserve0() external view returns (uint112);`
5050

51-
- **description**: Returns the initial reserve amount of asset 0.
51+
- **description**: Returns the equilibrium reserve amount of asset 0.
5252

53-
#### `initialReserve1() external view returns (uint112);`
53+
#### `equilibriumReserve1() external view returns (uint112);`
5454

55-
- **description**: Returns the initial reserve amount of asset 1.
55+
- **description**: Returns the equilibrium reserve amount of asset 1.
5656

5757
#### `feeMultiplier() external view returns (uint256);`
5858

@@ -66,11 +66,11 @@ The `IEulerSwap` interface defines the core functionality for executing token sw
6666

6767
#### `priceX() external view returns (uint256);`
6868

69-
- **description**: Returns the price of asset X in terms of asset Y.
69+
- **description**: Returns the marginal price of asset X in terms of asset Y at the equilibrium point.
7070

7171
#### `priceY() external view returns (uint256);`
7272

73-
- **description**: Returns the price of asset Y in terms of asset X.
73+
- **description**: Returns the marginal price of asset Y in terms of asset X at the equilibrium point.
7474

7575
#### `concentrationX() external view returns (uint256);`
7676

src/EulerSwap.sol

Lines changed: 21 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,8 @@ contract EulerSwap is IEulerSwap, EVCUtil {
2121
address public immutable asset0;
2222
address public immutable asset1;
2323
address public immutable eulerAccount;
24-
uint112 public immutable debtLimit0;
25-
uint112 public immutable debtLimit1;
26-
uint112 public immutable initialReserve0;
27-
uint112 public immutable initialReserve1;
24+
uint112 public immutable equilibriumReserve0;
25+
uint112 public immutable equilibriumReserve1;
2826
uint256 public immutable feeMultiplier;
2927

3028
uint256 public immutable priceX;
@@ -68,7 +66,6 @@ contract EulerSwap is IEulerSwap, EVCUtil {
6866
// EulerSwap params
6967

7068
require(params.fee < 1e18, BadParam());
71-
require(params.debtLimit0 <= type(uint112).max && params.debtLimit1 <= type(uint112).max, BadParam());
7269
require(curveParams.priceX > 0 && curveParams.priceY > 0, BadParam());
7370
require(curveParams.priceX <= 1e36 && curveParams.priceY <= 1e36, BadParam());
7471
require(curveParams.concentrationX <= 1e18 && curveParams.concentrationY <= 1e18, BadParam());
@@ -83,10 +80,10 @@ contract EulerSwap is IEulerSwap, EVCUtil {
8380
asset0 = asset0Addr;
8481
asset1 = asset1Addr;
8582
eulerAccount = params.eulerAccount;
86-
debtLimit0 = params.debtLimit0;
87-
debtLimit1 = params.debtLimit1;
88-
initialReserve0 = reserve0 = offsetReserve(params.debtLimit0, params.vault0);
89-
initialReserve1 = reserve1 = offsetReserve(params.debtLimit1, params.vault1);
83+
equilibriumReserve0 = params.equilibriumReserve0;
84+
equilibriumReserve1 = params.equilibriumReserve1;
85+
reserve0 = params.currReserve0;
86+
reserve1 = params.currReserve1;
9087
feeMultiplier = 1e18 - params.fee;
9188

9289
// Curve params
@@ -96,6 +93,12 @@ contract EulerSwap is IEulerSwap, EVCUtil {
9693
concentrationX = curveParams.concentrationX;
9794
concentrationY = curveParams.concentrationY;
9895

96+
// Validate reserves
97+
98+
require(verify(equilibriumReserve0, equilibriumReserve1), CurveViolation());
99+
require(verify(reserve0, reserve1), CurveViolation());
100+
require(!verify(reserve0 > 0 ? reserve0 - 1 : 0, reserve1 > 0 ? reserve1 - 1 : 0), CurveViolation());
101+
99102
emit EulerSwapCreated(asset0Addr, asset1Addr);
100103
}
101104

@@ -128,7 +131,6 @@ contract EulerSwap is IEulerSwap, EVCUtil {
128131
uint256 newReserve0 = reserve0 + amount0In - amount0Out;
129132
uint256 newReserve1 = reserve1 + amount1In - amount1Out;
130133

131-
require(newReserve0 <= type(uint112).max && newReserve1 <= type(uint112).max, Overflow());
132134
require(verify(newReserve0, newReserve1), CurveViolation());
133135

134136
reserve0 = uint112(newReserve0);
@@ -184,12 +186,16 @@ contract EulerSwap is IEulerSwap, EVCUtil {
184186

185187
/// @inheritdoc IEulerSwap
186188
function verify(uint256 newReserve0, uint256 newReserve1) public view returns (bool) {
187-
if (newReserve0 >= initialReserve0) {
188-
if (newReserve1 >= initialReserve1) return true;
189-
return newReserve0 >= f(newReserve1, priceY, priceX, initialReserve1, initialReserve0, concentrationY);
189+
if (newReserve0 > type(uint112).max || newReserve1 > type(uint112).max) return false;
190+
191+
if (newReserve0 >= equilibriumReserve0) {
192+
if (newReserve1 >= equilibriumReserve1) return true;
193+
return
194+
newReserve0 >= f(newReserve1, priceY, priceX, equilibriumReserve1, equilibriumReserve0, concentrationY);
190195
} else {
191-
if (newReserve1 < initialReserve1) return false;
192-
return newReserve1 >= f(newReserve0, priceX, priceY, initialReserve0, initialReserve1, concentrationX);
196+
if (newReserve1 < equilibriumReserve1) return false;
197+
return
198+
newReserve1 >= f(newReserve0, priceX, priceY, equilibriumReserve0, equilibriumReserve1, concentrationX);
193199
}
194200
}
195201

@@ -237,20 +243,6 @@ contract EulerSwap is IEulerSwap, EVCUtil {
237243
return shares == 0 ? 0 : IEVault(vault).convertToAssets(shares);
238244
}
239245

240-
function offsetReserve(uint112 reserve, address vault) internal view returns (uint112) {
241-
uint256 offset;
242-
uint256 debt = myDebt(vault);
243-
244-
if (debt != 0) {
245-
offset = reserve > debt ? reserve - debt : 0;
246-
} else {
247-
offset = reserve + myBalance(vault);
248-
}
249-
250-
require(offset <= type(uint112).max, Overflow());
251-
return uint112(offset);
252-
}
253-
254246
/// @dev EulerSwap curve definition
255247
/// Pre-conditions: x <= x0, 1 <= {px,py} <= 1e36, {x0,y0} <= type(uint112).max, c <= 1e18
256248
function f(uint256 x, uint256 px, uint256 py, uint256 x0, uint256 y0, uint256 c) internal pure returns (uint256) {

src/EulerSwapPeriphery.sol

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -214,29 +214,31 @@ contract EulerSwapPeriphery is IEulerSwapPeriphery {
214214
pure
215215
returns (uint256)
216216
{
217-
// A component of the quadratic formula
218-
uint256 A = 2 * c;
217+
unchecked {
218+
// A component of the quadratic formula: a = 2 * c
219+
uint256 A = 2 * c;
219220

220-
// B component of the quadratic formula
221-
int256 B = int256((px * (y - y0) + py - 1) / py) - int256((x0 * (2 * c - 1e18) + 1e18 - 1) / 1e18);
221+
// B component of the quadratic formula
222+
int256 B = int256((px * (y - y0) + py - 1) / py) - int256((x0 * (2 * c - 1e18) + 1e18 - 1) / 1e18);
222223

223-
// B^2 component, using FullMath for overflow safety
224-
uint256 absB = B < 0 ? uint256(-B) : uint256(B);
225-
uint256 squaredB = Math.mulDiv(absB, absB, 1e18, Math.Rounding.Ceil);
224+
// B^2 component, using FullMath for overflow safety
225+
uint256 absB = B < 0 ? uint256(-B) : uint256(B);
226+
uint256 squaredB = Math.mulDiv(absB, absB, 1e18, Math.Rounding.Ceil);
226227

227-
// 4 * A * C component of the quadratic formula
228-
uint256 AC4a = Math.mulDiv(4 * c, (1e18 - c), 1e18, Math.Rounding.Ceil);
229-
uint256 AC4b = Math.mulDiv(x0, x0, 1e18, Math.Rounding.Ceil);
230-
uint256 AC4 = Math.mulDiv(AC4a, AC4b, 1e18, Math.Rounding.Ceil);
228+
// 4 * A * C component of the quadratic formula
229+
uint256 AC4a = Math.mulDiv(4 * c, (1e18 - c), 1e18, Math.Rounding.Ceil);
230+
uint256 AC4b = Math.mulDiv(x0, x0, 1e18, Math.Rounding.Ceil);
231+
uint256 AC4 = Math.mulDiv(AC4a, AC4b, 1e18, Math.Rounding.Ceil);
231232

232-
// Discriminant: b^2 + 4ac, scaled up to maintain precision
233-
uint256 discriminant = (squaredB + AC4) * 1e18;
233+
// Discriminant: b^2 + 4ac, scaled up to maintain precision
234+
uint256 discriminant = (squaredB + AC4) * 1e18;
234235

235-
// Square root of the discriminant (rounded up)
236-
uint256 sqrt = Math.sqrt(discriminant);
237-
sqrt = (sqrt * sqrt < discriminant) ? sqrt + 1 : sqrt;
236+
// Square root of the discriminant (rounded up)
237+
uint256 sqrt = Math.sqrt(discriminant);
238+
sqrt = (sqrt * sqrt < discriminant) ? sqrt + 1 : sqrt;
238239

239-
// Compute and return x = fInverse(y) using the quadratic formula
240-
return Math.mulDiv(uint256(int256(sqrt) - B), 1e18, A, Math.Rounding.Ceil);
240+
// Compute and return x = fInverse(y) using the quadratic formula
241+
return Math.mulDiv(uint256(int256(sqrt) - B), 1e18, A, Math.Rounding.Ceil);
242+
}
241243
}
242244
}

src/interfaces/IEulerSwap.sol

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@ interface IEulerSwap {
66
address vault0;
77
address vault1;
88
address eulerAccount;
9-
uint112 debtLimit0;
10-
uint112 debtLimit1;
9+
uint112 equilibriumReserve0;
10+
uint112 equilibriumReserve1;
11+
uint112 currReserve0;
12+
uint112 currReserve1;
1113
uint256 fee;
1214
}
1315

@@ -46,8 +48,8 @@ interface IEulerSwap {
4648
function asset0() external view returns (address);
4749
function asset1() external view returns (address);
4850
function eulerAccount() external view returns (address);
49-
function initialReserve0() external view returns (uint112);
50-
function initialReserve1() external view returns (uint112);
51+
function equilibriumReserve0() external view returns (uint112);
52+
function equilibriumReserve1() external view returns (uint112);
5153
function feeMultiplier() external view returns (uint256);
5254
function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 status);
5355

test/AltDecimals.t.sol

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ contract AltDecimals is EulerSwapTestBase {
1111
}
1212

1313
function test_alt_decimals_6_18_in() public {
14-
eulerSwap = createEulerSwap(50e6, 50e18, 0, 1e18, 1e6, 0.9e18, 0.9e18);
14+
eulerSwap = createEulerSwap(50e6, 60e18, 0, 1e18, 1e6, 0.9e18, 0.9e18);
1515
skimAll(eulerSwap, true);
1616

1717
uint256 amount = 1e6;
@@ -31,7 +31,7 @@ contract AltDecimals is EulerSwapTestBase {
3131
}
3232

3333
function test_alt_decimals_6_18_out() public {
34-
eulerSwap = createEulerSwap(50e6, 50e18, 0, 1e18, 1e6, 0.9e18, 0.9e18);
34+
eulerSwap = createEulerSwap(50e6, 60e18, 0, 1e18, 1e6, 0.9e18, 0.9e18);
3535
skimAll(eulerSwap, true);
3636

3737
uint256 amount = 1e18;
@@ -51,7 +51,7 @@ contract AltDecimals is EulerSwapTestBase {
5151
}
5252

5353
function test_alt_decimals_18_6_in() public {
54-
eulerSwap = createEulerSwap(50e18, 50e6, 0, 1e6, 1e18, 0.9e18, 0.9e18);
54+
eulerSwap = createEulerSwap(60e18, 50e6, 0, 1e6, 1e18, 0.9e18, 0.9e18);
5555
skimAll(eulerSwap, true);
5656

5757
uint256 amount = 1e18;
@@ -71,7 +71,7 @@ contract AltDecimals is EulerSwapTestBase {
7171
}
7272

7373
function test_alt_decimals_18_6_out() public {
74-
eulerSwap = createEulerSwap(50e18, 50e6, 0, 1e6, 1e18, 0.9e18, 0.9e18);
74+
eulerSwap = createEulerSwap(60e18, 50e6, 0, 1e6, 1e18, 0.9e18, 0.9e18);
7575
skimAll(eulerSwap, false);
7676

7777
uint256 amount = 1e6;

test/DepositFailures.t.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ contract DepositFailuresTest is EulerSwapTestBase {
1313
function setUp() public virtual override {
1414
super.setUp();
1515

16-
eulerSwap = createEulerSwap(50e18, 50e18, 0, 1e18, 1e18, 0.4e18, 0.85e18);
16+
eulerSwap = createEulerSwap(60e18, 60e18, 0, 1e18, 1e18, 0.4e18, 0.85e18);
1717
}
1818

1919
function test_griefing() public monotonicHolderNAV {

test/EulerSwapFactoryTest.t.sol

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ contract EulerSwapFactoryTest is EulerSwapTestBase {
2020
uint256 allPoolsLengthBefore = eulerSwapFactory.allPoolsLength();
2121

2222
bytes32 salt = bytes32(uint256(1234));
23-
IEulerSwap.Params memory poolParams = IEulerSwap.Params(address(eTST), address(eTST2), holder, 1e18, 1e18, 0);
23+
IEulerSwap.Params memory poolParams =
24+
IEulerSwap.Params(address(eTST), address(eTST2), holder, 1e18, 1e18, 1e18, 1e18, 0);
2425
IEulerSwap.CurveParams memory curveParams = IEulerSwap.CurveParams(0.4e18, 0.85e18, 1e18, 1e18);
2526

2627
address predictedAddress = predictPoolAddress(address(eulerSwapFactory), poolParams, curveParams, salt);
@@ -73,7 +74,8 @@ contract EulerSwapFactoryTest is EulerSwapTestBase {
7374

7475
function testDeployWithAssetsOutOfOrderOrEqual() public {
7576
bytes32 salt = bytes32(uint256(1234));
76-
IEulerSwap.Params memory poolParams = IEulerSwap.Params(address(eTST), address(eTST), holder, 1e18, 1e18, 0);
77+
IEulerSwap.Params memory poolParams =
78+
IEulerSwap.Params(address(eTST), address(eTST), holder, 1e18, 1e18, 1e18, 1e18, 0);
7779
IEulerSwap.CurveParams memory curveParams = IEulerSwap.CurveParams(0.4e18, 0.85e18, 1e18, 1e18);
7880

7981
vm.prank(holder);
@@ -83,7 +85,8 @@ contract EulerSwapFactoryTest is EulerSwapTestBase {
8385

8486
function testDeployWithBadFee() public {
8587
bytes32 salt = bytes32(uint256(1234));
86-
IEulerSwap.Params memory poolParams = IEulerSwap.Params(address(eTST), address(eTST2), holder, 1e18, 1e18, 1e18);
88+
IEulerSwap.Params memory poolParams =
89+
IEulerSwap.Params(address(eTST), address(eTST2), holder, 1e18, 1e18, 1e18, 1e18, 1e18);
8790
IEulerSwap.CurveParams memory curveParams = IEulerSwap.CurveParams(0.4e18, 0.85e18, 1e18, 1e18);
8891

8992
vm.prank(holder);

test/EulerSwapPeriphery.t.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ contract EulerSwapPeripheryTest is EulerSwapTestBase {
1111
function setUp() public virtual override {
1212
super.setUp();
1313

14-
eulerSwap = createEulerSwap(50e18, 50e18, 0, 1e18, 1e18, 0.4e18, 0.85e18);
14+
eulerSwap = createEulerSwap(60e18, 60e18, 0, 1e18, 1e18, 0.4e18, 0.85e18);
1515

1616
IEulerSwap.Params memory params = getEulerSwapParams(50e18, 50e18, 0.4e18);
1717
IEulerSwap.CurveParams memory curveParams =

0 commit comments

Comments
 (0)