Skip to content

Commit f75bca9

Browse files
Merge pull request #29 from euler-xyz/update-eulerswapfactory
Update EulerSwapFactory
2 parents 95b8206 + 8d74ce3 commit f75bca9

File tree

5 files changed

+137
-120
lines changed

5 files changed

+137
-120
lines changed

src/EulerSwap.sol

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ contract EulerSwap is IEulerSwap, EVCUtil {
3636
uint112 public reserve1;
3737
uint32 public status; // 0 = unactivated, 1 = unlocked, 2 = locked
3838

39-
event EulerSwapCreated(address indexed eulerSwap, address indexed asset0, address indexed asset1);
39+
event EulerSwapCreated(address indexed asset0, address indexed asset1);
4040
event Swap(
4141
address indexed sender,
4242
uint256 amount0In,
@@ -96,7 +96,7 @@ contract EulerSwap is IEulerSwap, EVCUtil {
9696
concentrationX = curveParams.concentrationX;
9797
concentrationY = curveParams.concentrationY;
9898

99-
emit EulerSwapCreated(address(this), asset0Addr, asset1Addr);
99+
emit EulerSwapCreated(asset0Addr, asset1Addr);
100100
}
101101

102102
/// @inheritdoc IEulerSwap

src/EulerSwapFactory.sol

Lines changed: 57 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
// SPDX-License-Identifier: UNLICENSED
22
pragma solidity ^0.8.27;
33

4-
import {IEulerSwapFactory} from "./interfaces/IEulerSwapFactory.sol";
5-
import {IEulerSwap, EulerSwap} from "./EulerSwap.sol";
4+
import {IEulerSwapFactory, IEulerSwap} from "./interfaces/IEulerSwapFactory.sol";
5+
import {EulerSwap} from "./EulerSwap.sol";
6+
import {EVCUtil} from "ethereum-vault-connector/utils/EVCUtil.sol";
67

78
/// @title EulerSwapFactory contract
89
/// @custom:security-contact [email protected]
910
/// @author Euler Labs (https://www.eulerlabs.com/)
10-
contract EulerSwapFactory is IEulerSwapFactory {
11+
contract EulerSwapFactory is IEulerSwapFactory, EVCUtil {
1112
/// @dev An array to store all pools addresses.
1213
address[] public allPools;
13-
/// @dev Mapping to store pool addresses
14-
mapping(bytes32 poolKey => address pool) public getPool;
14+
/// @dev Mapping between a swap account and deployed pool that is currently set as operator
15+
mapping(address swapAccount => address operator) public swapAccountToPool;
1516

1617
event PoolDeployed(
1718
address indexed asset0,
@@ -28,64 +29,45 @@ contract EulerSwapFactory is IEulerSwapFactory {
2829
);
2930

3031
error InvalidQuery();
31-
error AlreadyDeployed();
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.swapAccount,
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-
);
32+
error Unauthorized();
33+
error OldOperatorStillInstalled();
34+
error OperatorNotInstalled();
5135

52-
address poolAsset0 = pool.asset0();
53-
address poolAsset1 = pool.asset1();
54-
uint256 feeMultiplier = pool.feeMultiplier();
55-
56-
{
57-
bytes32 poolKey = keccak256(
58-
abi.encode(
59-
poolAsset0,
60-
poolAsset1,
61-
params.vault0,
62-
params.vault1,
63-
params.swapAccount,
64-
feeMultiplier,
65-
params.priceX,
66-
params.priceY,
67-
params.concentrationX,
68-
params.concentrationY
69-
)
70-
);
71-
72-
require(getPool[poolKey] == address(0), AlreadyDeployed());
73-
74-
getPool[poolKey] = address(pool);
75-
allPools.push(address(pool));
76-
}
36+
constructor(address evc) EVCUtil(evc) {}
37+
38+
/// @notice Deploy a new EulerSwap pool with the given parameters
39+
/// @dev The pool address is deterministically generated using CREATE2 with a salt derived from
40+
/// the swap account address and provided salt parameter. This allows the pool address to be
41+
/// predicted before deployment.
42+
/// @param params Core pool parameters including vaults, account, and fee settings
43+
/// @param curveParams Parameters defining the curve shape including prices and concentrations
44+
/// @param salt Unique value to generate deterministic pool address
45+
/// @return Address of the newly deployed pool
46+
function deployPool(IEulerSwap.Params memory params, IEulerSwap.CurveParams memory curveParams, bytes32 salt)
47+
external
48+
returns (address)
49+
{
50+
require(_msgSender() == params.myAccount, Unauthorized());
51+
52+
EulerSwap pool = new EulerSwap{salt: keccak256(abi.encode(params.myAccount, salt))}(params, curveParams);
53+
54+
checkSwapAccountOperators(params.myAccount, address(pool));
55+
56+
allPools.push(address(pool));
57+
58+
EulerSwap(pool).activate();
7759

7860
emit PoolDeployed(
79-
poolAsset0,
80-
poolAsset1,
61+
pool.asset0(),
62+
pool.asset1(),
8163
params.vault0,
8264
params.vault1,
83-
feeMultiplier,
84-
params.swapAccount,
85-
params.priceX,
86-
params.priceY,
87-
params.concentrationX,
88-
params.concentrationY,
65+
pool.feeMultiplier(),
66+
params.myAccount,
67+
curveParams.priceX,
68+
curveParams.priceY,
69+
curveParams.concentrationX,
70+
curveParams.concentrationY,
8971
address(pool)
9072
);
9173

@@ -114,4 +96,21 @@ contract EulerSwapFactory is IEulerSwapFactory {
11496

11597
return allPoolsList;
11698
}
99+
100+
/// @notice Validates operator authorization for a swap account. First checks if the account has an existing operator
101+
/// and ensures it is deauthorized. Then verifies the new pool is authorized as an operator. Finally, updates the
102+
/// mapping to track the new pool as the account's operator.
103+
/// @param swapAccount The address of the swap account.
104+
/// @param newPool The address of the new pool.
105+
function checkSwapAccountOperators(address swapAccount, address newPool) internal {
106+
address operator = swapAccountToPool[swapAccount];
107+
108+
if (operator != address(0)) {
109+
require(!evc.isAccountOperatorAuthorized(swapAccount, operator), OldOperatorStillInstalled());
110+
}
111+
112+
require(evc.isAccountOperatorAuthorized(swapAccount, newPool), OperatorNotInstalled());
113+
114+
swapAccountToPool[swapAccount] = newPool;
115+
}
117116
}

src/interfaces/IEulerSwapFactory.sol

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,14 @@
11
// SPDX-License-Identifier: UNLICENSED
22
pragma solidity >=0.8.0;
33

4-
interface IEulerSwapFactory {
5-
struct DeployParams {
6-
address vault0;
7-
address vault1;
8-
address swapAccount;
9-
uint256 fee;
10-
uint256 priceX;
11-
uint256 priceY;
12-
uint256 concentrationX;
13-
uint256 concentrationY;
14-
uint112 debtLimit0;
15-
uint112 debtLimit1;
16-
}
4+
import {IEulerSwap} from "./IEulerSwap.sol";
175

18-
function deployPool(DeployParams memory params) external returns (address);
6+
interface IEulerSwapFactory {
7+
function deployPool(IEulerSwap.Params memory params, IEulerSwap.CurveParams memory curveParams, bytes32 salt)
8+
external
9+
returns (address);
1910

2011
function allPools(uint256 index) external view returns (address);
21-
function getPool(bytes32 poolKey) external view returns (address);
2212
function allPoolsLength() external view returns (uint256);
2313
function getAllPoolsListSlice(uint256 start, uint256 end) external view returns (address[] memory);
2414
}

test/EulerSwapFactoryTest.t.sol

Lines changed: 72 additions & 44 deletions
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 {EulerSwapTestBase, EulerSwap} from "./EulerSwapTestBase.t.sol";
4+
import {EulerSwapTestBase, IEulerSwap, IEVC, EulerSwap} from "./EulerSwapTestBase.t.sol";
55
import {EulerSwapFactory, IEulerSwapFactory} from "../src/EulerSwapFactory.sol";
66

77
contract EulerSwapFactoryTest is EulerSwapTestBase {
@@ -13,53 +13,57 @@ contract EulerSwapFactoryTest is EulerSwapTestBase {
1313
super.setUp();
1414

1515
vm.prank(creator);
16-
eulerSwapFactory = new EulerSwapFactory();
16+
eulerSwapFactory = new EulerSwapFactory(address(evc));
1717
}
1818

1919
function testDeployPool() public {
2020
uint256 allPoolsLengthBefore = eulerSwapFactory.allPoolsLength();
2121

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-
);
22+
bytes32 salt = bytes32(uint256(1234));
23+
IEulerSwap.Params memory poolParams = IEulerSwap.Params(address(eTST), address(eTST2), holder, 1e18, 1e18, 0);
24+
IEulerSwap.CurveParams memory curveParams = IEulerSwap.CurveParams(0.4e18, 0.85e18, 1e18, 1e18);
3025

31-
uint256 allPoolsLengthAfter = eulerSwapFactory.allPoolsLength();
32-
bytes32 poolKey = keccak256(
33-
abi.encode(
34-
eulerSwap.asset0(),
35-
eulerSwap.asset1(),
36-
eulerSwap.vault0(),
37-
eulerSwap.vault1(),
38-
eulerSwap.myAccount(),
39-
eulerSwap.feeMultiplier(),
40-
eulerSwap.priceX(),
41-
eulerSwap.priceY(),
42-
eulerSwap.concentrationX(),
43-
eulerSwap.concentrationY()
44-
)
45-
);
26+
address predictedAddress = predictPoolAddress(address(eulerSwapFactory), poolParams, curveParams, salt);
4627

28+
IEVC.BatchItem[] memory items = new IEVC.BatchItem[](2);
29+
30+
items[0] = IEVC.BatchItem({
31+
onBehalfOfAccount: address(0),
32+
targetContract: address(evc),
33+
value: 0,
34+
data: abi.encodeCall(evc.setAccountOperator, (holder, predictedAddress, true))
35+
});
36+
items[1] = IEVC.BatchItem({
37+
onBehalfOfAccount: holder,
38+
targetContract: address(eulerSwapFactory),
39+
value: 0,
40+
data: abi.encodeCall(EulerSwapFactory.deployPool, (poolParams, curveParams, salt))
41+
});
42+
43+
vm.prank(holder);
44+
evc.batch(items);
45+
46+
EulerSwap eulerSwap = EulerSwap(eulerSwapFactory.swapAccountToPool(holder));
47+
48+
uint256 allPoolsLengthAfter = eulerSwapFactory.allPoolsLength();
4749
assertEq(allPoolsLengthAfter - allPoolsLengthBefore, 1);
48-
assertEq(eulerSwapFactory.getPool(poolKey), address(eulerSwap));
49-
assertEq(eulerSwapFactory.getPool(poolKey), address(eulerSwap));
5050

5151
address[] memory poolsList = eulerSwapFactory.getAllPoolsListSlice(0, type(uint256).max);
5252
assertEq(poolsList.length, 1);
5353
assertEq(poolsList[0], address(eulerSwap));
5454
assertEq(eulerSwapFactory.allPools(0), address(eulerSwap));
5555

56-
vm.prank(creator);
57-
vm.expectRevert(EulerSwapFactory.AlreadyDeployed.selector);
58-
eulerSwapFactory.deployPool(
59-
IEulerSwapFactory.DeployParams(
60-
address(eTST), address(eTST2), holder, 0, 1e18, 1e18, 0.4e18, 0.85e18, 50e18, 50e18
61-
)
62-
);
56+
items = new IEVC.BatchItem[](1);
57+
items[0] = IEVC.BatchItem({
58+
onBehalfOfAccount: holder,
59+
targetContract: address(eulerSwapFactory),
60+
value: 0,
61+
data: abi.encodeCall(EulerSwapFactory.deployPool, (poolParams, curveParams, bytes32(uint256(12345))))
62+
});
63+
64+
vm.prank(holder);
65+
vm.expectRevert(EulerSwapFactory.OldOperatorStillInstalled.selector);
66+
evc.batch(items);
6367
}
6468

6569
function testInvalidGetAllPoolsListSliceQuery() public {
@@ -68,21 +72,45 @@ contract EulerSwapFactoryTest is EulerSwapTestBase {
6872
}
6973

7074
function testDeployWithAssetsOutOfOrderOrEqual() public {
71-
vm.prank(creator);
75+
bytes32 salt = bytes32(uint256(1234));
76+
IEulerSwap.Params memory poolParams = IEulerSwap.Params(address(eTST), address(eTST), holder, 1e18, 1e18, 0);
77+
IEulerSwap.CurveParams memory curveParams = IEulerSwap.CurveParams(0.4e18, 0.85e18, 1e18, 1e18);
78+
79+
vm.prank(holder);
7280
vm.expectRevert(EulerSwap.AssetsOutOfOrderOrEqual.selector);
73-
eulerSwapFactory.deployPool(
74-
IEulerSwapFactory.DeployParams(
75-
address(eTST), address(eTST), holder, 0, 1e18, 1e18, 0.4e18, 0.85e18, 50e18, 50e18
76-
)
77-
);
81+
eulerSwapFactory.deployPool(poolParams, curveParams, salt);
7882
}
7983

8084
function testDeployWithBadFee() public {
81-
vm.prank(creator);
85+
bytes32 salt = bytes32(uint256(1234));
86+
IEulerSwap.Params memory poolParams = IEulerSwap.Params(address(eTST), address(eTST2), holder, 1e18, 1e18, 1e18);
87+
IEulerSwap.CurveParams memory curveParams = IEulerSwap.CurveParams(0.4e18, 0.85e18, 1e18, 1e18);
88+
89+
vm.prank(holder);
8290
vm.expectRevert(EulerSwap.BadParam.selector);
83-
eulerSwapFactory.deployPool(
84-
IEulerSwapFactory.DeployParams(
85-
address(eTST), address(eTST2), holder, 1e18, 1e18, 1e18, 0.4e18, 0.85e18, 50e18, 50e18
91+
eulerSwapFactory.deployPool(poolParams, curveParams, salt);
92+
}
93+
94+
function predictPoolAddress(
95+
address factoryAddress,
96+
IEulerSwap.Params memory poolParams,
97+
IEulerSwap.CurveParams memory curveParams,
98+
bytes32 salt
99+
) internal pure returns (address) {
100+
return address(
101+
uint160(
102+
uint256(
103+
keccak256(
104+
abi.encodePacked(
105+
bytes1(0xff),
106+
factoryAddress,
107+
keccak256(abi.encode(address(poolParams.myAccount), salt)),
108+
keccak256(
109+
abi.encodePacked(type(EulerSwap).creationCode, abi.encode(poolParams, curveParams))
110+
)
111+
)
112+
)
113+
)
86114
)
87115
);
88116
}

test/EulerSwapTestBase.t.sol

Lines changed: 1 addition & 1 deletion
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 {IEulerSwap, EulerSwap} from "../src/EulerSwap.sol";
7+
import {IEulerSwap, IEVC, EulerSwap} from "../src/EulerSwap.sol";
88
import {EulerSwapPeriphery} from "../src/EulerSwapPeriphery.sol";
99

1010
contract EulerSwapTestBase is EVaultTestBase {

0 commit comments

Comments
 (0)