Skip to content

Commit b945455

Browse files
Merge pull request #7 from euler-xyz/feat/factory
Feat: factory
2 parents 8f0c306 + 9e1ab2d commit b945455

File tree

8 files changed

+235
-20
lines changed

8 files changed

+235
-20
lines changed

foundry.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,8 @@ src = "src"
33
out = "out"
44
libs = ["lib"]
55
solc = "0.8.27"
6+
optimizer = true
7+
optimizer_runs = 10000
8+
gas_reports = ["*"]
69

710
# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options

src/EulerSwap.sol

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,22 +9,6 @@ import {IAllowanceTransfer} from "permit2/src/interfaces/IAllowanceTransfer.sol"
99
import {EVCUtil} from "evc/utils/EVCUtil.sol";
1010

1111
contract EulerSwap is IEulerSwap, EVCUtil {
12-
struct Params {
13-
address vault0;
14-
address vault1;
15-
address myAccount;
16-
uint112 debtLimit0;
17-
uint112 debtLimit1;
18-
uint256 fee;
19-
}
20-
21-
struct CurveParams {
22-
uint256 priceX;
23-
uint256 priceY;
24-
uint256 concentrationX;
25-
uint256 concentrationY;
26-
}
27-
2812
bytes32 public constant curve = keccak256("EulerSwap v1");
2913

3014
address public immutable vault0;

src/EulerSwapFactory.sol

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
// SPDX-License-Identifier: UNLICENSED
2+
pragma solidity ^0.8.27;
3+
4+
import {IEulerSwapFactory} from "./interfaces/IEulerSwapFactory.sol";
5+
import {IEulerSwap, EulerSwap} from "./EulerSwap.sol";
6+
import {Ownable} from "openzeppelin-contracts/access/Ownable.sol";
7+
8+
/// @title EulerSwapFactory contract
9+
/// @custom:security-contact [email protected]
10+
/// @author Euler Labs (https://www.eulerlabs.com/)
11+
contract EulerSwapFactory is IEulerSwapFactory, Ownable {
12+
/// @dev An array to store all pools addresses.
13+
address[] public allPools;
14+
/// @dev Mapping to store pool addresses
15+
mapping(bytes32 poolKey => address pool) public getPool;
16+
17+
event PoolDeployed(
18+
address indexed asset0,
19+
address indexed asset1,
20+
uint256 indexed feeMultiplier,
21+
address swapAccount,
22+
uint256 priceX,
23+
uint256 priceY,
24+
uint256 concentrationX,
25+
uint256 concentrationY,
26+
address pool
27+
);
28+
29+
error InvalidQuery();
30+
31+
constructor() Ownable(msg.sender) {}
32+
33+
/// @notice Deploy EulerSwap pool.
34+
function deployPool(DeployParams memory params) external returns (address) {
35+
EulerSwap pool = new EulerSwap(
36+
IEulerSwap.Params({
37+
vault0: params.vault0,
38+
vault1: params.vault1,
39+
myAccount: params.holder,
40+
debtLimit0: params.debtLimit0,
41+
debtLimit1: params.debtLimit1,
42+
fee: params.fee
43+
}),
44+
IEulerSwap.CurveParams({
45+
priceX: params.priceX,
46+
priceY: params.priceY,
47+
concentrationX: params.concentrationX,
48+
concentrationY: params.concentrationY
49+
})
50+
);
51+
52+
address poolAsset0 = pool.asset0();
53+
address poolAsset1 = pool.asset1();
54+
uint256 feeMultiplier = pool.feeMultiplier();
55+
56+
bytes32 poolKey = keccak256(
57+
abi.encode(
58+
poolAsset0,
59+
poolAsset1,
60+
feeMultiplier,
61+
params.holder,
62+
params.priceX,
63+
params.priceY,
64+
params.concentrationX,
65+
params.concentrationY
66+
)
67+
);
68+
69+
getPool[poolKey] = address(pool);
70+
allPools.push(address(pool));
71+
72+
emit PoolDeployed(
73+
poolAsset0,
74+
poolAsset1,
75+
feeMultiplier,
76+
params.holder,
77+
params.priceX,
78+
params.priceY,
79+
params.concentrationX,
80+
params.concentrationY,
81+
address(pool)
82+
);
83+
84+
return address(pool);
85+
}
86+
87+
/// @notice Get the length of `allPools` array.
88+
/// @return `allPools` length.
89+
function allPoolsLength() external view returns (uint256) {
90+
return allPools.length;
91+
}
92+
93+
/// @notice Get a slice of the deployed pools array.
94+
/// @param _start Start index of the slice.
95+
/// @param _end End index of the slice.
96+
/// @return An array containing the slice of the deployed pools.
97+
function getAllPoolsListSlice(uint256 _start, uint256 _end) external view returns (address[] memory) {
98+
uint256 length = allPools.length;
99+
if (_end == type(uint256).max) _end = length;
100+
if (_end < _start || _end > length) revert InvalidQuery();
101+
102+
address[] memory allPoolsList = new address[](_end - _start);
103+
for (uint256 i; i < _end - _start; ++i) {
104+
allPoolsList[i] = allPools[_start + i];
105+
}
106+
107+
return allPoolsList;
108+
}
109+
}

src/interfaces/IEulerSwap.sol

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,22 @@
22
pragma solidity >=0.8.0;
33

44
interface IEulerSwap {
5+
struct Params {
6+
address vault0;
7+
address vault1;
8+
address myAccount;
9+
uint112 debtLimit0;
10+
uint112 debtLimit1;
11+
uint256 fee;
12+
}
13+
14+
struct CurveParams {
15+
uint256 priceX;
16+
uint256 priceY;
17+
uint256 concentrationX;
18+
uint256 concentrationY;
19+
}
20+
521
/// @notice Optimistically sends the requested amounts of tokens to the `to`
622
/// address, invokes `uniswapV2Call` callback on `to` (if `data` was provided),
723
/// and then verifies that a sufficient amount of tokens were transferred to

src/interfaces/IEulerSwapFactory.sol

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// SPDX-License-Identifier: UNLICENSED
2+
pragma solidity >=0.8.0;
3+
4+
interface IEulerSwapFactory {
5+
struct DeployParams {
6+
address vault0;
7+
address vault1;
8+
address holder;
9+
uint256 fee;
10+
uint256 priceX;
11+
uint256 priceY;
12+
uint256 concentrationX;
13+
uint256 concentrationY;
14+
uint112 debtLimit0;
15+
uint112 debtLimit1;
16+
}
17+
18+
function deployPool(DeployParams memory params) external returns (address);
19+
20+
function allPools(uint256 index) external view returns (address);
21+
function getPool(bytes32 poolKey) external view returns (address);
22+
function allPoolsLength() external view returns (uint256);
23+
function getAllPoolsListSlice(uint256 start, uint256 end) external view returns (address[] memory);
24+
}

test/EulerSwapFactoryTest.t.sol

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
// SPDX-License-Identifier: GPL-2.0-or-later
2+
pragma solidity ^0.8.24;
3+
4+
import {EulerSwapTestBase, EulerSwap} from "./EulerSwapTestBase.t.sol";
5+
import {EulerSwapFactory, IEulerSwapFactory} from "../src/EulerSwapFactory.sol";
6+
7+
contract EulerSwapFactoryTest is EulerSwapTestBase {
8+
EulerSwapFactory public eulerSwapFactory;
9+
10+
uint256 minFee = 0.0000000000001e18;
11+
12+
function setUp() public virtual override {
13+
super.setUp();
14+
15+
vm.prank(creator);
16+
eulerSwapFactory = new EulerSwapFactory();
17+
}
18+
19+
function testDeployPool() public {
20+
uint256 allPoolsLengthBefore = eulerSwapFactory.allPoolsLength();
21+
22+
vm.prank(creator);
23+
EulerSwap eulerSwap = EulerSwap(
24+
eulerSwapFactory.deployPool(
25+
IEulerSwapFactory.DeployParams(
26+
address(eTST), address(eTST2), holder, 0, 1e18, 1e18, 0.4e18, 0.85e18, 50e18, 50e18
27+
)
28+
)
29+
);
30+
31+
uint256 allPoolsLengthAfter = eulerSwapFactory.allPoolsLength();
32+
bytes32 poolKey = keccak256(
33+
abi.encode(
34+
eulerSwap.asset0(),
35+
eulerSwap.asset1(),
36+
eulerSwap.feeMultiplier(),
37+
eulerSwap.myAccount(),
38+
eulerSwap.priceX(),
39+
eulerSwap.priceY(),
40+
eulerSwap.concentrationX(),
41+
eulerSwap.concentrationY()
42+
)
43+
);
44+
45+
assertEq(allPoolsLengthAfter - allPoolsLengthBefore, 1);
46+
assertEq(eulerSwapFactory.getPool(poolKey), address(eulerSwap));
47+
assertEq(eulerSwapFactory.getPool(poolKey), address(eulerSwap));
48+
49+
address[] memory poolsList = eulerSwapFactory.getAllPoolsListSlice(0, type(uint256).max);
50+
assertEq(poolsList.length, 1);
51+
assertEq(poolsList[0], address(eulerSwap));
52+
assertEq(eulerSwapFactory.allPools(0), address(eulerSwap));
53+
}
54+
55+
function testInvalidGetAllPoolsListSliceQuery() public {
56+
vm.expectRevert(EulerSwapFactory.InvalidQuery.selector);
57+
eulerSwapFactory.getAllPoolsListSlice(1, 0);
58+
}
59+
60+
function testDeployWithAssetsOutOfOrderOrEqual() public {
61+
vm.prank(creator);
62+
vm.expectRevert(EulerSwap.AssetsOutOfOrderOrEqual.selector);
63+
eulerSwapFactory.deployPool(
64+
IEulerSwapFactory.DeployParams(
65+
address(eTST), address(eTST), holder, 0, 1e18, 1e18, 0.4e18, 0.85e18, 50e18, 50e18
66+
)
67+
);
68+
}
69+
70+
function testDeployWithBadFee() public {
71+
vm.prank(creator);
72+
vm.expectRevert(EulerSwap.BadFee.selector);
73+
eulerSwapFactory.deployPool(
74+
IEulerSwapFactory.DeployParams(
75+
address(eTST), address(eTST2), holder, 1e18, 1e18, 1e18, 0.4e18, 0.85e18, 50e18, 50e18
76+
)
77+
);
78+
}
79+
}

test/EulerSwapTest.t.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// SPDX-License-Identifier: GPL-2.0-or-later
22
pragma solidity ^0.8.24;
33

4-
import {IEVault, EulerSwapTestBase, EulerSwap, TestERC20} from "./EulerSwapTestBase.t.sol";
4+
import {IEVault, IEulerSwap, EulerSwapTestBase, EulerSwap, TestERC20} from "./EulerSwapTestBase.t.sol";
55

66
contract EulerSwapTest is EulerSwapTestBase {
77
EulerSwap public eulerSwap;

test/EulerSwapTestBase.t.sol

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ pragma solidity ^0.8.24;
44
import {Test, console} from "forge-std/Test.sol";
55
import {EVaultTestBase, TestERC20} from "evk-test/unit/evault/EVaultTestBase.t.sol";
66
import {IEVault} from "evk/EVault/IEVault.sol";
7-
import {EulerSwap} from "../src/EulerSwap.sol";
7+
import {IEulerSwap, EulerSwap} from "../src/EulerSwap.sol";
88
import {EulerSwapPeriphery} from "../src/EulerSwapPeriphery.sol";
99

1010
contract EulerSwapTestBase is EVaultTestBase {
@@ -87,7 +87,7 @@ contract EulerSwapTestBase is EVaultTestBase {
8787
vm.prank(creator);
8888
EulerSwap eulerSwap = new EulerSwap(
8989
getEulerSwapParams(debtLimitA, debtLimitB, fee),
90-
EulerSwap.CurveParams({priceX: px, priceY: py, concentrationX: cx, concentrationY: cy})
90+
IEulerSwap.CurveParams({priceX: px, priceY: py, concentrationX: cx, concentrationY: cy})
9191
);
9292

9393
vm.prank(holder);
@@ -146,7 +146,7 @@ contract EulerSwapTestBase is EVaultTestBase {
146146
view
147147
returns (EulerSwap.Params memory)
148148
{
149-
return EulerSwap.Params({
149+
return IEulerSwap.Params({
150150
vault0: address(eTST),
151151
vault1: address(eTST2),
152152
myAccount: holder,

0 commit comments

Comments
 (0)