From 16c81b3f88a54fa3dd62256027247302a728a424 Mon Sep 17 00:00:00 2001 From: Haythem Sellami <17862704+haythemsellami@users.noreply.github.com> Date: Thu, 19 Dec 2024 12:56:05 +0700 Subject: [PATCH 01/13] MaglevEulerSwapFactory --- src/MaglevEulerSwapFactory.sol | 97 ++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 src/MaglevEulerSwapFactory.sol diff --git a/src/MaglevEulerSwapFactory.sol b/src/MaglevEulerSwapFactory.sol new file mode 100644 index 0000000..61b3b06 --- /dev/null +++ b/src/MaglevEulerSwapFactory.sol @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.27; + +import {Ownable} from "openzeppelin-contracts/access/Ownable.sol"; +import {MaglevEulerSwap as Maglev, MaglevBase} from "./MaglevEulerSwap.sol"; + +/// @title MaglevEulerSwapRegistry contract +/// @custom:security-contact security@euler.xyz +/// @author Euler Labs (https://www.eulerlabs.com/) +contract MaglevEulerSwapFactory is Ownable { + event PoolDeployed(address indexed asset0, address indexed asset1, uint256 indexed feeMultiplier, address pool); + + error InvalidQuery(); + error PoolAlreadyDeployed(); + + /// @dev EVC address. + address public immutable evc; + /// @dev An array to store all pools addresses. + address[] public allPools; + /// @dev Mapping from asset0/asset1/fee => pool address. + mapping(address => mapping(address => mapping(uint256 => address))) public getPool; + + constructor(address evcAddr) Ownable(msg.sender) { + evc = evcAddr; + } + + /// @notice Deploy EulerSwap pool. + function deployPool( + address vault0, + address vault1, + address holder, + uint112 debtLimit0, + uint112 debtLimit1, + uint256 fee, + uint256 priceX, + uint256 priceY, + uint256 concentrationX, + uint256 concentrationY + ) external onlyOwner returns (address) { + Maglev pool = new Maglev( + MaglevBase.BaseParams({ + evc: address(evc), + vault0: vault0, + vault1: vault1, + myAccount: holder, + debtLimit0: debtLimit0, + debtLimit1: debtLimit1, + fee: fee + }), + Maglev.EulerSwapParams({ + priceX: priceX, + priceY: priceY, + concentrationX: concentrationX, + concentrationY: concentrationY + }) + ); + + address poolAsset0 = pool.asset0(); + address poolAsset1 = pool.asset1(); + uint256 feeMultiplier = pool.feeMultiplier(); + + require(getPool[poolAsset0][poolAsset1][feeMultiplier] == address(0), PoolAlreadyDeployed()); + + getPool[poolAsset0][poolAsset1][feeMultiplier] = address(pool); + // populate mapping in the reverse direction, deliberate choice to avoid the cost of comparing addresses + getPool[poolAsset1][poolAsset0][feeMultiplier] = address(pool); + + allPools.push(address(pool)); + + emit PoolDeployed(poolAsset0, poolAsset1, feeMultiplier, address(pool)); + + return address(pool); + } + + /// @notice Get the length of `allPools` array. + /// @return `allPools` length. + function allPoolsLength() external view returns (uint256) { + return allPools.length; + } + + /// @notice Get a slice of the registered pools array. + /// @param _start Start index of the slice. + /// @param _end End index of the slice. + /// @return An array containing the slice of the registered pools. + function getAllPoolsListSlice(uint256 _start, uint256 _end) external view returns (address[] memory) { + uint256 length = allPools.length; + if (_end == type(uint256).max) _end = length; + if (_end < _start || _end > length) revert InvalidQuery(); + + address[] memory allPoolsList = new address[](_end - _start); + for (uint256 i; i < _end - _start; ++i) { + allPoolsList[i] = allPools[_start + i]; + } + + return allPoolsList; + } +} From cdfbfbe274ce24a96e56f685a7f1f0a19e7ccee5 Mon Sep 17 00:00:00 2001 From: Haythem Sellami <17862704+haythemsellami@users.noreply.github.com> Date: Thu, 19 Dec 2024 12:56:32 +0700 Subject: [PATCH 02/13] test --- test/MaglevEulerSwapFactoryTest.t.sol | 58 +++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 test/MaglevEulerSwapFactoryTest.t.sol diff --git a/test/MaglevEulerSwapFactoryTest.t.sol b/test/MaglevEulerSwapFactoryTest.t.sol new file mode 100644 index 0000000..01e137c --- /dev/null +++ b/test/MaglevEulerSwapFactoryTest.t.sol @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity ^0.8.24; + +// import {Test, console} from "forge-std/Test.sol"; +// import {TestERC20} from "evk-test/unit/evault/EVaultTestBase.t.sol"; +// import {IEVault} from "evk/EVault/IEVault.sol"; +import {MaglevTestBase} from "./MaglevTestBase.t.sol"; +import {MaglevEulerSwap as Maglev} from "../src/MaglevEulerSwap.sol"; +import {MaglevEulerSwapFactory} from "../src/MaglevEulerSwapFactory.sol"; + +contract MaglevEulerSwapFactoryTest is MaglevTestBase { + MaglevEulerSwapFactory public eulerSwapFactory; + + uint256 minFee = 0.0000000000001e18; + + function setUp() public virtual override { + super.setUp(); + + vm.prank(creator); + eulerSwapFactory = new MaglevEulerSwapFactory(address(evc)); + } + + function testDeployPool() public { + uint256 allPoolsLengthBefore = eulerSwapFactory.allPoolsLength(); + + vm.prank(creator); + Maglev maglev = Maglev( + eulerSwapFactory.deployPool( + address(eTST), address(eTST2), holder, 50e18, 50e18, 0, 1e18, 1e18, 0.4e18, 0.85e18 + ) + ); + + uint256 allPoolsLengthAfter = eulerSwapFactory.allPoolsLength(); + + assertEq(allPoolsLengthAfter - allPoolsLengthBefore, 1); + assertEq(eulerSwapFactory.getPool(maglev.asset0(), maglev.asset1(), maglev.feeMultiplier()), address(maglev)); + assertEq(eulerSwapFactory.getPool(maglev.asset1(), maglev.asset0(), maglev.feeMultiplier()), address(maglev)); + + address[] memory poolsList = eulerSwapFactory.getAllPoolsListSlice(0, type(uint256).max); + assertEq(poolsList.length, 1); + assertEq(poolsList[0], address(maglev)); + assertEq(eulerSwapFactory.allPools(0), address(maglev)); + } + + function testDeployPoolWhenAldreadyRegistered() public { + vm.prank(creator); + eulerSwapFactory.deployPool(address(eTST), address(eTST2), holder, 50e18, 50e18, 0, 1e18, 1e18, 0.4e18, 0.85e18); + + vm.prank(creator); + vm.expectRevert(MaglevEulerSwapFactory.PoolAlreadyDeployed.selector); + eulerSwapFactory.deployPool(address(eTST), address(eTST2), holder, 50e18, 50e18, 0, 1e18, 1e18, 0.4e18, 0.85e18); + } + + function testInvalidGetAllPoolsListSliceQuery() public { + vm.expectRevert(MaglevEulerSwapFactory.InvalidQuery.selector); + eulerSwapFactory.getAllPoolsListSlice(1, 0); + } +} From ade9485c90c95f87c1f64c59130eb270d9fab2c5 Mon Sep 17 00:00:00 2001 From: Haythem Sellami <17862704+haythemsellami@users.noreply.github.com> Date: Thu, 19 Dec 2024 12:58:52 +0700 Subject: [PATCH 03/13] typo --- src/MaglevEulerSwapFactory.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/MaglevEulerSwapFactory.sol b/src/MaglevEulerSwapFactory.sol index 61b3b06..a6cd1c2 100644 --- a/src/MaglevEulerSwapFactory.sol +++ b/src/MaglevEulerSwapFactory.sol @@ -78,10 +78,10 @@ contract MaglevEulerSwapFactory is Ownable { return allPools.length; } - /// @notice Get a slice of the registered pools array. + /// @notice Get a slice of the deployed pools array. /// @param _start Start index of the slice. /// @param _end End index of the slice. - /// @return An array containing the slice of the registered pools. + /// @return An array containing the slice of the deployed pools. function getAllPoolsListSlice(uint256 _start, uint256 _end) external view returns (address[] memory) { uint256 length = allPools.length; if (_end == type(uint256).max) _end = length; From 40391c5a3ba453234844218ac7aca51807c66ada Mon Sep 17 00:00:00 2001 From: Haythem Sellami <17862704+haythemsellami@users.noreply.github.com> Date: Thu, 19 Dec 2024 13:04:50 +0700 Subject: [PATCH 04/13] add tests --- test/MaglevEulerSwapFactoryTest.t.sol | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/test/MaglevEulerSwapFactoryTest.t.sol b/test/MaglevEulerSwapFactoryTest.t.sol index 01e137c..2d3c0a0 100644 --- a/test/MaglevEulerSwapFactoryTest.t.sol +++ b/test/MaglevEulerSwapFactoryTest.t.sol @@ -5,7 +5,7 @@ pragma solidity ^0.8.24; // import {TestERC20} from "evk-test/unit/evault/EVaultTestBase.t.sol"; // import {IEVault} from "evk/EVault/IEVault.sol"; import {MaglevTestBase} from "./MaglevTestBase.t.sol"; -import {MaglevEulerSwap as Maglev} from "../src/MaglevEulerSwap.sol"; +import {MaglevEulerSwap as Maglev, MaglevBase} from "../src/MaglevEulerSwap.sol"; import {MaglevEulerSwapFactory} from "../src/MaglevEulerSwapFactory.sol"; contract MaglevEulerSwapFactoryTest is MaglevTestBase { @@ -55,4 +55,18 @@ contract MaglevEulerSwapFactoryTest is MaglevTestBase { vm.expectRevert(MaglevEulerSwapFactory.InvalidQuery.selector); eulerSwapFactory.getAllPoolsListSlice(1, 0); } + + function testDeployWithUnsupportedPair() public { + vm.prank(creator); + vm.expectRevert(MaglevBase.UnsupportedPair.selector); + eulerSwapFactory.deployPool(address(eTST), address(eTST), holder, 50e18, 50e18, 0, 1e18, 1e18, 0.4e18, 0.85e18); + } + + function testDeployWithBadFee() public { + vm.prank(creator); + vm.expectRevert(MaglevBase.BadFee.selector); + eulerSwapFactory.deployPool( + address(eTST), address(eTST2), holder, 50e18, 50e18, 1e18, 1e18, 1e18, 0.4e18, 0.85e18 + ); + } } From 94e072d010a931cc91743e61e7d2947ea0d4707b Mon Sep 17 00:00:00 2001 From: Haythem Sellami <17862704+haythemsellami@users.noreply.github.com> Date: Fri, 20 Dec 2024 12:54:22 +0700 Subject: [PATCH 05/13] add interface --- src/MaglevEulerSwapFactory.sol | 3 ++- src/interfaces/IMaglevEulerSwapFactory.sol | 23 ++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 src/interfaces/IMaglevEulerSwapFactory.sol diff --git a/src/MaglevEulerSwapFactory.sol b/src/MaglevEulerSwapFactory.sol index a6cd1c2..5f647b8 100644 --- a/src/MaglevEulerSwapFactory.sol +++ b/src/MaglevEulerSwapFactory.sol @@ -1,13 +1,14 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.27; +import {IMaglevEulerSwapFactory} from "./interfaces/IMaglevEulerSwapFactory.sol"; import {Ownable} from "openzeppelin-contracts/access/Ownable.sol"; import {MaglevEulerSwap as Maglev, MaglevBase} from "./MaglevEulerSwap.sol"; /// @title MaglevEulerSwapRegistry contract /// @custom:security-contact security@euler.xyz /// @author Euler Labs (https://www.eulerlabs.com/) -contract MaglevEulerSwapFactory is Ownable { +contract MaglevEulerSwapFactory is IMaglevEulerSwapFactory, Ownable { event PoolDeployed(address indexed asset0, address indexed asset1, uint256 indexed feeMultiplier, address pool); error InvalidQuery(); diff --git a/src/interfaces/IMaglevEulerSwapFactory.sol b/src/interfaces/IMaglevEulerSwapFactory.sol new file mode 100644 index 0000000..4978e5d --- /dev/null +++ b/src/interfaces/IMaglevEulerSwapFactory.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity >=0.8.0; + +interface IMaglevEulerSwapFactory { + function deployPool( + address vault0, + address vault1, + address holder, + uint112 debtLimit0, + uint112 debtLimit1, + uint256 fee, + uint256 priceX, + uint256 priceY, + uint256 concentrationX, + uint256 concentrationY + ) external returns (address); + + function evc() external view returns (address); + function allPools(uint256 index) external view returns (address); + function getPool(address assetA, address assetB, uint256 fee) external view returns (address); + function allPoolsLength() external view returns (uint256); + function getAllPoolsListSlice(uint256 start, uint256 end) external view returns (address[] memory); +} From e69004a97a26031be200cc44fab9ade35290a470 Mon Sep 17 00:00:00 2001 From: Haythem Sellami <17862704+haythemsellami@users.noreply.github.com> Date: Wed, 15 Jan 2025 15:59:35 +0700 Subject: [PATCH 06/13] feat: allow re-deploying pool with same asset0/asset1/fee --- src/MaglevEulerSwapFactory.sol | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/MaglevEulerSwapFactory.sol b/src/MaglevEulerSwapFactory.sol index 5f647b8..0f1906c 100644 --- a/src/MaglevEulerSwapFactory.sol +++ b/src/MaglevEulerSwapFactory.sol @@ -60,8 +60,6 @@ contract MaglevEulerSwapFactory is IMaglevEulerSwapFactory, Ownable { address poolAsset1 = pool.asset1(); uint256 feeMultiplier = pool.feeMultiplier(); - require(getPool[poolAsset0][poolAsset1][feeMultiplier] == address(0), PoolAlreadyDeployed()); - getPool[poolAsset0][poolAsset1][feeMultiplier] = address(pool); // populate mapping in the reverse direction, deliberate choice to avoid the cost of comparing addresses getPool[poolAsset1][poolAsset0][feeMultiplier] = address(pool); From db650f3d1169e98eab5580f70245e2cdeee99d45 Mon Sep 17 00:00:00 2001 From: Haythem Sellami <17862704+haythemsellami@users.noreply.github.com> Date: Wed, 15 Jan 2025 16:00:07 +0700 Subject: [PATCH 07/13] clean' --- src/MaglevEulerSwapFactory.sol | 1 - test/MaglevEulerSwapFactoryTest.t.sol | 9 --------- 2 files changed, 10 deletions(-) diff --git a/src/MaglevEulerSwapFactory.sol b/src/MaglevEulerSwapFactory.sol index 0f1906c..a9ce3c5 100644 --- a/src/MaglevEulerSwapFactory.sol +++ b/src/MaglevEulerSwapFactory.sol @@ -12,7 +12,6 @@ contract MaglevEulerSwapFactory is IMaglevEulerSwapFactory, Ownable { event PoolDeployed(address indexed asset0, address indexed asset1, uint256 indexed feeMultiplier, address pool); error InvalidQuery(); - error PoolAlreadyDeployed(); /// @dev EVC address. address public immutable evc; diff --git a/test/MaglevEulerSwapFactoryTest.t.sol b/test/MaglevEulerSwapFactoryTest.t.sol index 2d3c0a0..3f5df30 100644 --- a/test/MaglevEulerSwapFactoryTest.t.sol +++ b/test/MaglevEulerSwapFactoryTest.t.sol @@ -42,15 +42,6 @@ contract MaglevEulerSwapFactoryTest is MaglevTestBase { assertEq(eulerSwapFactory.allPools(0), address(maglev)); } - function testDeployPoolWhenAldreadyRegistered() public { - vm.prank(creator); - eulerSwapFactory.deployPool(address(eTST), address(eTST2), holder, 50e18, 50e18, 0, 1e18, 1e18, 0.4e18, 0.85e18); - - vm.prank(creator); - vm.expectRevert(MaglevEulerSwapFactory.PoolAlreadyDeployed.selector); - eulerSwapFactory.deployPool(address(eTST), address(eTST2), holder, 50e18, 50e18, 0, 1e18, 1e18, 0.4e18, 0.85e18); - } - function testInvalidGetAllPoolsListSliceQuery() public { vm.expectRevert(MaglevEulerSwapFactory.InvalidQuery.selector); eulerSwapFactory.getAllPoolsListSlice(1, 0); From 69245d03b9f68530b8bfa1045e6293acbdd94226 Mon Sep 17 00:00:00 2001 From: Haythem Sellami <17862704+haythemsellami@users.noreply.github.com> Date: Fri, 24 Jan 2025 16:08:21 +0700 Subject: [PATCH 08/13] update factory --- foundry.toml | 3 +++ src/MaglevEulerSwapFactory.sol | 21 ++++++++++++++------- src/interfaces/IMaglevEulerSwapFactory.sol | 8 ++++---- test/MaglevEulerSwapFactoryTest.t.sol | 22 +++++++++++++++++----- 4 files changed, 38 insertions(+), 16 deletions(-) diff --git a/foundry.toml b/foundry.toml index fc855a1..d8142ba 100644 --- a/foundry.toml +++ b/foundry.toml @@ -3,5 +3,8 @@ src = "src" out = "out" libs = ["lib"] solc = "0.8.27" +optimizer = true +optimizer_runs = 800 +gas_reports = ["*"] # See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options diff --git a/src/MaglevEulerSwapFactory.sol b/src/MaglevEulerSwapFactory.sol index a9ce3c5..e92b3b5 100644 --- a/src/MaglevEulerSwapFactory.sol +++ b/src/MaglevEulerSwapFactory.sol @@ -18,7 +18,7 @@ contract MaglevEulerSwapFactory is IMaglevEulerSwapFactory, Ownable { /// @dev An array to store all pools addresses. address[] public allPools; /// @dev Mapping from asset0/asset1/fee => pool address. - mapping(address => mapping(address => mapping(uint256 => address))) public getPool; + mapping(bytes32 => address) public getPool; constructor(address evcAddr) Ownable(msg.sender) { evc = evcAddr; @@ -29,13 +29,13 @@ contract MaglevEulerSwapFactory is IMaglevEulerSwapFactory, Ownable { address vault0, address vault1, address holder, - uint112 debtLimit0, - uint112 debtLimit1, uint256 fee, uint256 priceX, uint256 priceY, uint256 concentrationX, - uint256 concentrationY + uint256 concentrationY, + uint112 debtLimit0, + uint112 debtLimit1 ) external onlyOwner returns (address) { Maglev pool = new Maglev( MaglevBase.BaseParams({ @@ -58,11 +58,18 @@ contract MaglevEulerSwapFactory is IMaglevEulerSwapFactory, Ownable { address poolAsset0 = pool.asset0(); address poolAsset1 = pool.asset1(); uint256 feeMultiplier = pool.feeMultiplier(); + address myAccount = pool.myAccount(); + uint256 priceX = pool.priceX(); + uint256 priceY = pool.priceY(); + uint256 concentrationX = pool.concentrationX(); + uint256 concentrationY = pool.concentrationY(); - getPool[poolAsset0][poolAsset1][feeMultiplier] = address(pool); - // populate mapping in the reverse direction, deliberate choice to avoid the cost of comparing addresses - getPool[poolAsset1][poolAsset0][feeMultiplier] = address(pool); + bytes32 poolKey = keccak256( + abi.encode(poolAsset0, poolAsset1, feeMultiplier, myAccount, priceX, priceY, concentrationX, concentrationY) + ); + + getPool[poolKey] = address(pool); allPools.push(address(pool)); emit PoolDeployed(poolAsset0, poolAsset1, feeMultiplier, address(pool)); diff --git a/src/interfaces/IMaglevEulerSwapFactory.sol b/src/interfaces/IMaglevEulerSwapFactory.sol index 4978e5d..6017c10 100644 --- a/src/interfaces/IMaglevEulerSwapFactory.sol +++ b/src/interfaces/IMaglevEulerSwapFactory.sol @@ -6,18 +6,18 @@ interface IMaglevEulerSwapFactory { address vault0, address vault1, address holder, - uint112 debtLimit0, - uint112 debtLimit1, uint256 fee, uint256 priceX, uint256 priceY, uint256 concentrationX, - uint256 concentrationY + uint256 concentrationY, + uint112 debtLimit0, + uint112 debtLimit1 ) external returns (address); function evc() external view returns (address); function allPools(uint256 index) external view returns (address); - function getPool(address assetA, address assetB, uint256 fee) external view returns (address); + function getPool(bytes32 poolKey) external view returns (address); function allPoolsLength() external view returns (uint256); function getAllPoolsListSlice(uint256 start, uint256 end) external view returns (address[] memory); } diff --git a/test/MaglevEulerSwapFactoryTest.t.sol b/test/MaglevEulerSwapFactoryTest.t.sol index 3f5df30..0ae6f45 100644 --- a/test/MaglevEulerSwapFactoryTest.t.sol +++ b/test/MaglevEulerSwapFactoryTest.t.sol @@ -26,15 +26,27 @@ contract MaglevEulerSwapFactoryTest is MaglevTestBase { vm.prank(creator); Maglev maglev = Maglev( eulerSwapFactory.deployPool( - address(eTST), address(eTST2), holder, 50e18, 50e18, 0, 1e18, 1e18, 0.4e18, 0.85e18 + address(eTST), address(eTST2), holder, 0, 1e18, 1e18, 0.4e18, 0.85e18, 50e18, 50e18 ) ); uint256 allPoolsLengthAfter = eulerSwapFactory.allPoolsLength(); + bytes32 poolKey = keccak256( + abi.encode( + maglev.asset0(), + maglev.asset1(), + maglev.feeMultiplier(), + maglev.myAccount(), + maglev.priceX(), + maglev.priceY(), + maglev.concentrationX(), + maglev.concentrationY() + ) + ); assertEq(allPoolsLengthAfter - allPoolsLengthBefore, 1); - assertEq(eulerSwapFactory.getPool(maglev.asset0(), maglev.asset1(), maglev.feeMultiplier()), address(maglev)); - assertEq(eulerSwapFactory.getPool(maglev.asset1(), maglev.asset0(), maglev.feeMultiplier()), address(maglev)); + assertEq(eulerSwapFactory.getPool(poolKey), address(maglev)); + assertEq(eulerSwapFactory.getPool(poolKey), address(maglev)); address[] memory poolsList = eulerSwapFactory.getAllPoolsListSlice(0, type(uint256).max); assertEq(poolsList.length, 1); @@ -50,14 +62,14 @@ contract MaglevEulerSwapFactoryTest is MaglevTestBase { function testDeployWithUnsupportedPair() public { vm.prank(creator); vm.expectRevert(MaglevBase.UnsupportedPair.selector); - eulerSwapFactory.deployPool(address(eTST), address(eTST), holder, 50e18, 50e18, 0, 1e18, 1e18, 0.4e18, 0.85e18); + eulerSwapFactory.deployPool(address(eTST), address(eTST), holder, 0, 1e18, 1e18, 0.4e18, 0.85e18, 50e18, 50e18); } function testDeployWithBadFee() public { vm.prank(creator); vm.expectRevert(MaglevBase.BadFee.selector); eulerSwapFactory.deployPool( - address(eTST), address(eTST2), holder, 50e18, 50e18, 1e18, 1e18, 1e18, 0.4e18, 0.85e18 + address(eTST), address(eTST2), holder, 1e18, 1e18, 1e18, 0.4e18, 0.85e18, 50e18, 50e18 ); } } From 9193f596f9b7c61385e61308a940c3539d1fd4b7 Mon Sep 17 00:00:00 2001 From: Haythem Sellami <17862704+haythemsellami@users.noreply.github.com> Date: Fri, 24 Jan 2025 16:08:57 +0700 Subject: [PATCH 09/13] lint --- src/MaglevEulerSwapFactory.sol | 1 - 1 file changed, 1 deletion(-) diff --git a/src/MaglevEulerSwapFactory.sol b/src/MaglevEulerSwapFactory.sol index e92b3b5..b2b1be6 100644 --- a/src/MaglevEulerSwapFactory.sol +++ b/src/MaglevEulerSwapFactory.sol @@ -64,7 +64,6 @@ contract MaglevEulerSwapFactory is IMaglevEulerSwapFactory, Ownable { uint256 concentrationX = pool.concentrationX(); uint256 concentrationY = pool.concentrationY(); - bytes32 poolKey = keccak256( abi.encode(poolAsset0, poolAsset1, feeMultiplier, myAccount, priceX, priceY, concentrationX, concentrationY) ); From 651f1e28837dec290090ab400e8f0290dd140e91 Mon Sep 17 00:00:00 2001 From: Haythem Sellami <17862704+haythemsellami@users.noreply.github.com> Date: Mon, 17 Feb 2025 11:31:59 +0100 Subject: [PATCH 10/13] factory --- src/EulerSwap.sol | 17 ---- ...erSwapFactory.sol => EulerSwapFactory.sol} | 97 +++++++++++-------- src/interfaces/IEulerSwap.sol | 17 ++++ ...rSwapFactory.sol => IEulerSwapFactory.sol} | 28 +++--- test/EulerSwapFactoryTest.t.sol | 79 +++++++++++++++ test/EulerSwapTest.t.sol | 6 +- test/EulerSwapTestBase.t.sol | 6 +- test/MaglevEulerSwapFactoryTest.t.sol | 75 -------------- 8 files changed, 172 insertions(+), 153 deletions(-) rename src/{MaglevEulerSwapFactory.sol => EulerSwapFactory.sol} (52%) rename src/interfaces/{IMaglevEulerSwapFactory.sol => IEulerSwapFactory.sol} (51%) create mode 100644 test/EulerSwapFactoryTest.t.sol delete mode 100644 test/MaglevEulerSwapFactoryTest.t.sol diff --git a/src/EulerSwap.sol b/src/EulerSwap.sol index c47f120..5b7f413 100644 --- a/src/EulerSwap.sol +++ b/src/EulerSwap.sol @@ -8,23 +8,6 @@ import {IEulerSwap} from "./interfaces/IEulerSwap.sol"; import {EVCUtil} from "evc/utils/EVCUtil.sol"; contract EulerSwap is IEulerSwap, EVCUtil { - struct Params { - address evc; - address vault0; - address vault1; - address myAccount; - uint112 debtLimit0; - uint112 debtLimit1; - uint256 fee; - } - - struct CurveParams { - uint256 priceX; - uint256 priceY; - uint256 concentrationX; - uint256 concentrationY; - } - bytes32 public constant curve = keccak256("EulerSwap v1"); address public immutable vault0; diff --git a/src/MaglevEulerSwapFactory.sol b/src/EulerSwapFactory.sol similarity index 52% rename from src/MaglevEulerSwapFactory.sol rename to src/EulerSwapFactory.sol index b2b1be6..3c9b27b 100644 --- a/src/MaglevEulerSwapFactory.sol +++ b/src/EulerSwapFactory.sol @@ -1,77 +1,90 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.27; -import {IMaglevEulerSwapFactory} from "./interfaces/IMaglevEulerSwapFactory.sol"; +import {IEulerSwapFactory} from "./interfaces/IEulerSwapFactory.sol"; +import {IEulerSwap, EulerSwap} from "./EulerSwap.sol"; import {Ownable} from "openzeppelin-contracts/access/Ownable.sol"; -import {MaglevEulerSwap as Maglev, MaglevBase} from "./MaglevEulerSwap.sol"; -/// @title MaglevEulerSwapRegistry contract +/// @title EulerSwapFactory contract /// @custom:security-contact security@euler.xyz /// @author Euler Labs (https://www.eulerlabs.com/) -contract MaglevEulerSwapFactory is IMaglevEulerSwapFactory, Ownable { - event PoolDeployed(address indexed asset0, address indexed asset1, uint256 indexed feeMultiplier, address pool); - - error InvalidQuery(); - +contract EulerSwapFactory is IEulerSwapFactory, Ownable { /// @dev EVC address. address public immutable evc; /// @dev An array to store all pools addresses. address[] public allPools; - /// @dev Mapping from asset0/asset1/fee => pool address. - mapping(bytes32 => address) public getPool; + /// @dev Mapping to store pool addresses + mapping(bytes32 poolKey => address pool) public getPool; + + event PoolDeployed( + address indexed asset0, + address indexed asset1, + uint256 indexed feeMultiplier, + address swapAccount, + uint256 priceX, + uint256 priceY, + uint256 concentrationX, + uint256 concentrationY, + address pool + ); + + error InvalidQuery(); constructor(address evcAddr) Ownable(msg.sender) { evc = evcAddr; } /// @notice Deploy EulerSwap pool. - function deployPool( - address vault0, - address vault1, - address holder, - uint256 fee, - uint256 priceX, - uint256 priceY, - uint256 concentrationX, - uint256 concentrationY, - uint112 debtLimit0, - uint112 debtLimit1 - ) external onlyOwner returns (address) { - Maglev pool = new Maglev( - MaglevBase.BaseParams({ + function deployPool(DeployParams memory params) external returns (address) { + EulerSwap pool = new EulerSwap( + IEulerSwap.Params({ evc: address(evc), - vault0: vault0, - vault1: vault1, - myAccount: holder, - debtLimit0: debtLimit0, - debtLimit1: debtLimit1, - fee: fee + vault0: params.vault0, + vault1: params.vault1, + myAccount: params.holder, + debtLimit0: params.debtLimit0, + debtLimit1: params.debtLimit1, + fee: params.fee }), - Maglev.EulerSwapParams({ - priceX: priceX, - priceY: priceY, - concentrationX: concentrationX, - concentrationY: concentrationY + IEulerSwap.CurveParams({ + priceX: params.priceX, + priceY: params.priceY, + concentrationX: params.concentrationX, + concentrationY: params.concentrationY }) ); address poolAsset0 = pool.asset0(); address poolAsset1 = pool.asset1(); uint256 feeMultiplier = pool.feeMultiplier(); - address myAccount = pool.myAccount(); - uint256 priceX = pool.priceX(); - uint256 priceY = pool.priceY(); - uint256 concentrationX = pool.concentrationX(); - uint256 concentrationY = pool.concentrationY(); bytes32 poolKey = keccak256( - abi.encode(poolAsset0, poolAsset1, feeMultiplier, myAccount, priceX, priceY, concentrationX, concentrationY) + abi.encode( + poolAsset0, + poolAsset1, + feeMultiplier, + params.holder, + params.priceX, + params.priceY, + params.concentrationX, + params.concentrationY + ) ); getPool[poolKey] = address(pool); allPools.push(address(pool)); - emit PoolDeployed(poolAsset0, poolAsset1, feeMultiplier, address(pool)); + emit PoolDeployed( + poolAsset0, + poolAsset1, + feeMultiplier, + params.holder, + params.priceX, + params.priceY, + params.concentrationX, + params.concentrationY, + address(pool) + ); return address(pool); } diff --git a/src/interfaces/IEulerSwap.sol b/src/interfaces/IEulerSwap.sol index c9a9865..d533faf 100644 --- a/src/interfaces/IEulerSwap.sol +++ b/src/interfaces/IEulerSwap.sol @@ -2,6 +2,23 @@ pragma solidity >=0.8.0; interface IEulerSwap { + struct Params { + address evc; + address vault0; + address vault1; + address myAccount; + uint112 debtLimit0; + uint112 debtLimit1; + uint256 fee; + } + + struct CurveParams { + uint256 priceX; + uint256 priceY; + uint256 concentrationX; + uint256 concentrationY; + } + /// @notice Optimistically sends the requested amounts of tokens to the `to` /// address, invokes `uniswapV2Call` callback on `to` (if `data` was provided), /// and then verifies that a sufficient amount of tokens were transferred to diff --git a/src/interfaces/IMaglevEulerSwapFactory.sol b/src/interfaces/IEulerSwapFactory.sol similarity index 51% rename from src/interfaces/IMaglevEulerSwapFactory.sol rename to src/interfaces/IEulerSwapFactory.sol index 6017c10..00e2b97 100644 --- a/src/interfaces/IMaglevEulerSwapFactory.sol +++ b/src/interfaces/IEulerSwapFactory.sol @@ -1,19 +1,21 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity >=0.8.0; -interface IMaglevEulerSwapFactory { - function deployPool( - address vault0, - address vault1, - address holder, - uint256 fee, - uint256 priceX, - uint256 priceY, - uint256 concentrationX, - uint256 concentrationY, - uint112 debtLimit0, - uint112 debtLimit1 - ) external returns (address); +interface IEulerSwapFactory { + struct DeployParams { + address vault0; + address vault1; + address holder; + uint256 fee; + uint256 priceX; + uint256 priceY; + uint256 concentrationX; + uint256 concentrationY; + uint112 debtLimit0; + uint112 debtLimit1; + } + + function deployPool(DeployParams memory params) external returns (address); function evc() external view returns (address); function allPools(uint256 index) external view returns (address); diff --git a/test/EulerSwapFactoryTest.t.sol b/test/EulerSwapFactoryTest.t.sol new file mode 100644 index 0000000..5ea6cf8 --- /dev/null +++ b/test/EulerSwapFactoryTest.t.sol @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity ^0.8.24; + +import {EulerSwapTestBase, EulerSwap} from "./EulerSwapTestBase.t.sol"; +import {EulerSwapFactory, IEulerSwapFactory} from "../src/EulerSwapFactory.sol"; + +contract EulerSwapFactoryTest is EulerSwapTestBase { + EulerSwapFactory public eulerSwapFactory; + + uint256 minFee = 0.0000000000001e18; + + function setUp() public virtual override { + super.setUp(); + + vm.prank(creator); + eulerSwapFactory = new EulerSwapFactory(address(evc)); + } + + function testDeployPool() public { + uint256 allPoolsLengthBefore = eulerSwapFactory.allPoolsLength(); + + vm.prank(creator); + EulerSwap eulerSwap = EulerSwap( + eulerSwapFactory.deployPool( + IEulerSwapFactory.DeployParams( + address(eTST), address(eTST2), holder, 0, 1e18, 1e18, 0.4e18, 0.85e18, 50e18, 50e18 + ) + ) + ); + + uint256 allPoolsLengthAfter = eulerSwapFactory.allPoolsLength(); + bytes32 poolKey = keccak256( + abi.encode( + eulerSwap.asset0(), + eulerSwap.asset1(), + eulerSwap.feeMultiplier(), + eulerSwap.myAccount(), + eulerSwap.priceX(), + eulerSwap.priceY(), + eulerSwap.concentrationX(), + eulerSwap.concentrationY() + ) + ); + + assertEq(allPoolsLengthAfter - allPoolsLengthBefore, 1); + assertEq(eulerSwapFactory.getPool(poolKey), address(eulerSwap)); + assertEq(eulerSwapFactory.getPool(poolKey), address(eulerSwap)); + + address[] memory poolsList = eulerSwapFactory.getAllPoolsListSlice(0, type(uint256).max); + assertEq(poolsList.length, 1); + assertEq(poolsList[0], address(eulerSwap)); + assertEq(eulerSwapFactory.allPools(0), address(eulerSwap)); + } + + function testInvalidGetAllPoolsListSliceQuery() public { + vm.expectRevert(EulerSwapFactory.InvalidQuery.selector); + eulerSwapFactory.getAllPoolsListSlice(1, 0); + } + + function testDeployWithAssetsOutOfOrderOrEqual() public { + vm.prank(creator); + vm.expectRevert(EulerSwap.AssetsOutOfOrderOrEqual.selector); + eulerSwapFactory.deployPool( + IEulerSwapFactory.DeployParams( + address(eTST), address(eTST), holder, 0, 1e18, 1e18, 0.4e18, 0.85e18, 50e18, 50e18 + ) + ); + } + + function testDeployWithBadFee() public { + vm.prank(creator); + vm.expectRevert(EulerSwap.BadFee.selector); + eulerSwapFactory.deployPool( + IEulerSwapFactory.DeployParams( + address(eTST), address(eTST2), holder, 1e18, 1e18, 1e18, 0.4e18, 0.85e18, 50e18, 50e18 + ) + ); + } +} diff --git a/test/EulerSwapTest.t.sol b/test/EulerSwapTest.t.sol index d81e85f..9a9fb4e 100644 --- a/test/EulerSwapTest.t.sol +++ b/test/EulerSwapTest.t.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.24; -import {IEVault, EulerSwapTestBase, EulerSwap, TestERC20} from "./EulerSwapTestBase.t.sol"; +import {IEVault, IEulerSwap, EulerSwapTestBase, EulerSwap, TestERC20} from "./EulerSwapTestBase.t.sol"; contract EulerSwapTest is EulerSwapTestBase { EulerSwap public eulerSwap; @@ -16,7 +16,7 @@ contract EulerSwapTest is EulerSwapTestBase { vm.expectRevert(EulerSwap.DifferentEVC.selector); new EulerSwap( - EulerSwap.Params({ + IEulerSwap.Params({ evc: address(makeAddr("RANDOM_EVC")), vault0: address(eTST), vault1: address(eTST2), @@ -25,7 +25,7 @@ contract EulerSwapTest is EulerSwapTestBase { debtLimit1: 50e18, fee: 0 }), - EulerSwap.CurveParams({priceX: 1e18, priceY: 1e18, concentrationX: 4e18, concentrationY: 0.85e18}) + IEulerSwap.CurveParams({priceX: 1e18, priceY: 1e18, concentrationX: 4e18, concentrationY: 0.85e18}) ); } diff --git a/test/EulerSwapTestBase.t.sol b/test/EulerSwapTestBase.t.sol index eb13977..94fab06 100644 --- a/test/EulerSwapTestBase.t.sol +++ b/test/EulerSwapTestBase.t.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.24; import {Test, console} from "forge-std/Test.sol"; import {EVaultTestBase, TestERC20} from "evk-test/unit/evault/EVaultTestBase.t.sol"; import {IEVault} from "evk/EVault/IEVault.sol"; -import {EulerSwap} from "../src/EulerSwap.sol"; +import {IEulerSwap, EulerSwap} from "../src/EulerSwap.sol"; import {EulerSwapPeriphery} from "../src/EulerSwapPeriphery.sol"; contract EulerSwapTestBase is EVaultTestBase { @@ -87,7 +87,7 @@ contract EulerSwapTestBase is EVaultTestBase { vm.prank(creator); EulerSwap eulerSwap = new EulerSwap( getEulerSwapParams(debtLimitA, debtLimitB, fee), - EulerSwap.CurveParams({priceX: px, priceY: py, concentrationX: cx, concentrationY: cy}) + IEulerSwap.CurveParams({priceX: px, priceY: py, concentrationX: cx, concentrationY: cy}) ); vm.prank(holder); @@ -146,7 +146,7 @@ contract EulerSwapTestBase is EVaultTestBase { view returns (EulerSwap.Params memory) { - return EulerSwap.Params({ + return IEulerSwap.Params({ evc: address(evc), vault0: address(eTST), vault1: address(eTST2), diff --git a/test/MaglevEulerSwapFactoryTest.t.sol b/test/MaglevEulerSwapFactoryTest.t.sol deleted file mode 100644 index 0ae6f45..0000000 --- a/test/MaglevEulerSwapFactoryTest.t.sol +++ /dev/null @@ -1,75 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.8.24; - -// import {Test, console} from "forge-std/Test.sol"; -// import {TestERC20} from "evk-test/unit/evault/EVaultTestBase.t.sol"; -// import {IEVault} from "evk/EVault/IEVault.sol"; -import {MaglevTestBase} from "./MaglevTestBase.t.sol"; -import {MaglevEulerSwap as Maglev, MaglevBase} from "../src/MaglevEulerSwap.sol"; -import {MaglevEulerSwapFactory} from "../src/MaglevEulerSwapFactory.sol"; - -contract MaglevEulerSwapFactoryTest is MaglevTestBase { - MaglevEulerSwapFactory public eulerSwapFactory; - - uint256 minFee = 0.0000000000001e18; - - function setUp() public virtual override { - super.setUp(); - - vm.prank(creator); - eulerSwapFactory = new MaglevEulerSwapFactory(address(evc)); - } - - function testDeployPool() public { - uint256 allPoolsLengthBefore = eulerSwapFactory.allPoolsLength(); - - vm.prank(creator); - Maglev maglev = Maglev( - eulerSwapFactory.deployPool( - address(eTST), address(eTST2), holder, 0, 1e18, 1e18, 0.4e18, 0.85e18, 50e18, 50e18 - ) - ); - - uint256 allPoolsLengthAfter = eulerSwapFactory.allPoolsLength(); - bytes32 poolKey = keccak256( - abi.encode( - maglev.asset0(), - maglev.asset1(), - maglev.feeMultiplier(), - maglev.myAccount(), - maglev.priceX(), - maglev.priceY(), - maglev.concentrationX(), - maglev.concentrationY() - ) - ); - - assertEq(allPoolsLengthAfter - allPoolsLengthBefore, 1); - assertEq(eulerSwapFactory.getPool(poolKey), address(maglev)); - assertEq(eulerSwapFactory.getPool(poolKey), address(maglev)); - - address[] memory poolsList = eulerSwapFactory.getAllPoolsListSlice(0, type(uint256).max); - assertEq(poolsList.length, 1); - assertEq(poolsList[0], address(maglev)); - assertEq(eulerSwapFactory.allPools(0), address(maglev)); - } - - function testInvalidGetAllPoolsListSliceQuery() public { - vm.expectRevert(MaglevEulerSwapFactory.InvalidQuery.selector); - eulerSwapFactory.getAllPoolsListSlice(1, 0); - } - - function testDeployWithUnsupportedPair() public { - vm.prank(creator); - vm.expectRevert(MaglevBase.UnsupportedPair.selector); - eulerSwapFactory.deployPool(address(eTST), address(eTST), holder, 0, 1e18, 1e18, 0.4e18, 0.85e18, 50e18, 50e18); - } - - function testDeployWithBadFee() public { - vm.prank(creator); - vm.expectRevert(MaglevBase.BadFee.selector); - eulerSwapFactory.deployPool( - address(eTST), address(eTST2), holder, 1e18, 1e18, 1e18, 0.4e18, 0.85e18, 50e18, 50e18 - ); - } -} From baeb1742d828776fa09410b7319aa2179d8db237 Mon Sep 17 00:00:00 2001 From: Haythem Sellami <17862704+haythemsellami@users.noreply.github.com> Date: Mon, 17 Feb 2025 11:33:32 +0100 Subject: [PATCH 11/13] update config --- foundry.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/foundry.toml b/foundry.toml index d8142ba..4c9b590 100644 --- a/foundry.toml +++ b/foundry.toml @@ -4,7 +4,7 @@ out = "out" libs = ["lib"] solc = "0.8.27" optimizer = true -optimizer_runs = 800 +optimizer_runs = 10000 gas_reports = ["*"] # See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options From bbd8d80dc32c6e0bd7ed99d2824761c3d8b0102c Mon Sep 17 00:00:00 2001 From: Haythem Sellami <17862704+haythemsellami@users.noreply.github.com> Date: Mon, 17 Feb 2025 15:49:38 +0100 Subject: [PATCH 12/13] fix --- src/EulerSwapFactory.sol | 1 - src/interfaces/IEulerSwap.sol | 1 - test/EulerSwapTestBase.t.sol | 2 +- 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/EulerSwapFactory.sol b/src/EulerSwapFactory.sol index 3c9b27b..0dce633 100644 --- a/src/EulerSwapFactory.sol +++ b/src/EulerSwapFactory.sol @@ -38,7 +38,6 @@ contract EulerSwapFactory is IEulerSwapFactory, Ownable { function deployPool(DeployParams memory params) external returns (address) { EulerSwap pool = new EulerSwap( IEulerSwap.Params({ - evc: address(evc), vault0: params.vault0, vault1: params.vault1, myAccount: params.holder, diff --git a/src/interfaces/IEulerSwap.sol b/src/interfaces/IEulerSwap.sol index d533faf..b939815 100644 --- a/src/interfaces/IEulerSwap.sol +++ b/src/interfaces/IEulerSwap.sol @@ -3,7 +3,6 @@ pragma solidity >=0.8.0; interface IEulerSwap { struct Params { - address evc; address vault0; address vault1; address myAccount; diff --git a/test/EulerSwapTestBase.t.sol b/test/EulerSwapTestBase.t.sol index 8fb1547..dce25c4 100644 --- a/test/EulerSwapTestBase.t.sol +++ b/test/EulerSwapTestBase.t.sol @@ -146,7 +146,7 @@ contract EulerSwapTestBase is EVaultTestBase { view returns (EulerSwap.Params memory) { - return EulerSwap.Params({ + return IEulerSwap.Params({ vault0: address(eTST), vault1: address(eTST2), myAccount: holder, From 9e1ab2d41b2f006397b063f1c752daba319cbc4a Mon Sep 17 00:00:00 2001 From: Haythem Sellami <17862704+haythemsellami@users.noreply.github.com> Date: Mon, 17 Feb 2025 15:53:07 +0100 Subject: [PATCH 13/13] fix --- src/EulerSwapFactory.sol | 6 +----- src/interfaces/IEulerSwapFactory.sol | 1 - test/EulerSwapFactoryTest.t.sol | 2 +- 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/EulerSwapFactory.sol b/src/EulerSwapFactory.sol index 0dce633..7249472 100644 --- a/src/EulerSwapFactory.sol +++ b/src/EulerSwapFactory.sol @@ -9,8 +9,6 @@ import {Ownable} from "openzeppelin-contracts/access/Ownable.sol"; /// @custom:security-contact security@euler.xyz /// @author Euler Labs (https://www.eulerlabs.com/) contract EulerSwapFactory is IEulerSwapFactory, Ownable { - /// @dev EVC address. - address public immutable evc; /// @dev An array to store all pools addresses. address[] public allPools; /// @dev Mapping to store pool addresses @@ -30,9 +28,7 @@ contract EulerSwapFactory is IEulerSwapFactory, Ownable { error InvalidQuery(); - constructor(address evcAddr) Ownable(msg.sender) { - evc = evcAddr; - } + constructor() Ownable(msg.sender) {} /// @notice Deploy EulerSwap pool. function deployPool(DeployParams memory params) external returns (address) { diff --git a/src/interfaces/IEulerSwapFactory.sol b/src/interfaces/IEulerSwapFactory.sol index 00e2b97..87746c7 100644 --- a/src/interfaces/IEulerSwapFactory.sol +++ b/src/interfaces/IEulerSwapFactory.sol @@ -17,7 +17,6 @@ interface IEulerSwapFactory { function deployPool(DeployParams memory params) external returns (address); - function evc() external view returns (address); function allPools(uint256 index) external view returns (address); function getPool(bytes32 poolKey) external view returns (address); function allPoolsLength() external view returns (uint256); diff --git a/test/EulerSwapFactoryTest.t.sol b/test/EulerSwapFactoryTest.t.sol index 5ea6cf8..e939056 100644 --- a/test/EulerSwapFactoryTest.t.sol +++ b/test/EulerSwapFactoryTest.t.sol @@ -13,7 +13,7 @@ contract EulerSwapFactoryTest is EulerSwapTestBase { super.setUp(); vm.prank(creator); - eulerSwapFactory = new EulerSwapFactory(address(evc)); + eulerSwapFactory = new EulerSwapFactory(); } function testDeployPool() public {