Skip to content

Commit 3635092

Browse files
committed
fix issue with multiple uninstalls by using OZ's EnumerableSet library
1 parent d568fa0 commit 3635092

File tree

2 files changed

+28
-40
lines changed

2 files changed

+28
-40
lines changed

src/EulerSwapFactory.sol

Lines changed: 28 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// SPDX-License-Identifier: UNLICENSED
22
pragma solidity ^0.8.27;
33

4+
import {EnumerableSet} from "openzeppelin-contracts/utils/structs/EnumerableSet.sol";
5+
46
import {IEulerSwapFactory, IEulerSwap} from "./interfaces/IEulerSwapFactory.sol";
57
import {EVCUtil} from "ethereum-vault-connector/utils/EVCUtil.sol";
68
import {GenericFactory} from "evk/GenericFactory/GenericFactory.sol";
@@ -13,15 +15,19 @@ import {MetaProxyDeployer} from "./utils/MetaProxyDeployer.sol";
1315
/// @custom:security-contact [email protected]
1416
/// @author Euler Labs (https://www.eulerlabs.com/)
1517
contract EulerSwapFactory is IEulerSwapFactory, EVCUtil, ProtocolFee {
16-
/// @dev An array to store all pools addresses.
17-
address[] private allPools;
18+
using EnumerableSet for EnumerableSet.AddressSet;
19+
1820
/// @dev Vaults must be deployed by this factory
1921
address public immutable evkFactory;
2022
/// @dev The EulerSwap code instance that will be proxied to
2123
address public immutable eulerSwapImpl;
22-
/// @dev Mapping between euler account and EulerAccountState
23-
mapping(address eulerAccount => EulerAccountState state) private eulerAccountState;
24-
mapping(address asset0 => mapping(address asset1 => address[])) private poolMap;
24+
25+
/// @dev Mapping from euler account to pool, if installed
26+
mapping(address eulerAccount => address) internal installedPools;
27+
/// @dev Set of all pool addresses
28+
EnumerableSet.AddressSet internal allPools;
29+
/// @dev Mapping from sorted pair of underlyings to set of pools
30+
mapping(address asset0 => mapping(address asset1 => EnumerableSet.AddressSet)) internal poolMap;
2531

2632
event PoolDeployed(address indexed asset0, address indexed asset1, address indexed eulerAccount, address pool);
2733
event PoolConfig(address indexed pool, IEulerSwap.Params params, IEulerSwap.InitialState initialState);
@@ -102,12 +108,12 @@ contract EulerSwapFactory is IEulerSwapFactory, EVCUtil, ProtocolFee {
102108

103109
/// @inheritdoc IEulerSwapFactory
104110
function poolByEulerAccount(address eulerAccount) external view returns (address) {
105-
return eulerAccountState[eulerAccount].pool;
111+
return installedPools[eulerAccount];
106112
}
107113

108114
/// @inheritdoc IEulerSwapFactory
109115
function poolsLength() external view returns (uint256) {
110-
return allPools.length;
116+
return allPools.length();
111117
}
112118

113119
/// @inheritdoc IEulerSwapFactory
@@ -122,7 +128,7 @@ contract EulerSwapFactory is IEulerSwapFactory, EVCUtil, ProtocolFee {
122128

123129
/// @inheritdoc IEulerSwapFactory
124130
function poolsByPairLength(address asset0, address asset1) external view returns (uint256) {
125-
return poolMap[asset0][asset1].length;
131+
return poolMap[asset0][asset1].length();
126132
}
127133

128134
/// @inheritdoc IEulerSwapFactory
@@ -147,16 +153,10 @@ contract EulerSwapFactory is IEulerSwapFactory, EVCUtil, ProtocolFee {
147153

148154
(address asset0, address asset1) = _getAssets(newOperator);
149155

150-
address[] storage poolMapArray = poolMap[asset0][asset1];
156+
installedPools[eulerAccount] = newOperator;
151157

152-
eulerAccountState[eulerAccount] = EulerAccountState({
153-
pool: newOperator,
154-
allPoolsIndex: uint48(allPools.length),
155-
poolMapIndex: uint48(poolMapArray.length)
156-
});
157-
158-
allPools.push(newOperator);
159-
poolMapArray.push(newOperator);
158+
allPools.add(newOperator);
159+
poolMap[asset0][asset1].add(newOperator);
160160
}
161161

162162
/// @notice Uninstalls the pool associated with the given Euler account
@@ -165,32 +165,22 @@ contract EulerSwapFactory is IEulerSwapFactory, EVCUtil, ProtocolFee {
165165
/// @dev If no pool exists for the account, the function returns without any action
166166
/// @param eulerAccount The address of the Euler account whose pool should be uninstalled
167167
function uninstall(address eulerAccount) internal {
168-
address pool = eulerAccountState[eulerAccount].pool;
168+
address pool = installedPools[eulerAccount];
169169

170170
if (pool == address(0)) return;
171171

172172
require(!evc.isAccountOperatorAuthorized(eulerAccount, pool), OldOperatorStillInstalled());
173173

174174
(address asset0, address asset1) = _getAssets(pool);
175175

176-
address[] storage poolMapArr = poolMap[asset0][asset1];
177-
178-
swapAndPop(allPools, eulerAccountState[eulerAccount].allPoolsIndex);
179-
swapAndPop(poolMapArr, eulerAccountState[eulerAccount].poolMapIndex);
176+
allPools.remove(pool);
177+
poolMap[asset0][asset1].remove(pool);
180178

181-
delete eulerAccountState[eulerAccount];
179+
delete installedPools[eulerAccount];
182180

183181
emit PoolUninstalled(asset0, asset1, eulerAccount, pool);
184182
}
185183

186-
/// @notice Swaps the element at the given index with the last element and removes the last element
187-
/// @param arr The storage array to modify
188-
/// @param index The index of the element to remove
189-
function swapAndPop(address[] storage arr, uint256 index) internal {
190-
arr[index] = arr[arr.length - 1];
191-
arr.pop();
192-
}
193-
194184
/// @notice Retrieves the asset addresses for a given pool
195185
/// @dev Calls the pool contract to get its asset0 and asset1 addresses
196186
/// @param pool The address of the pool to query
@@ -206,14 +196,18 @@ contract EulerSwapFactory is IEulerSwapFactory, EVCUtil, ProtocolFee {
206196
/// @param start The starting index of the slice (inclusive)
207197
/// @param end The ending index of the slice (exclusive)
208198
/// @return A new memory array containing the requested slice of addresses
209-
function _getSlice(address[] storage arr, uint256 start, uint256 end) internal view returns (address[] memory) {
210-
uint256 length = arr.length;
199+
function _getSlice(EnumerableSet.AddressSet storage arr, uint256 start, uint256 end)
200+
internal
201+
view
202+
returns (address[] memory)
203+
{
204+
uint256 length = arr.length();
211205
if (end == type(uint256).max) end = length;
212206
if (end < start || end > length) revert SliceOutOfBounds();
213207

214208
address[] memory slice = new address[](end - start);
215209
for (uint256 i; i < end - start; ++i) {
216-
slice[i] = arr[start + i];
210+
slice[i] = arr.at(start + i);
217211
}
218212

219213
return slice;

src/interfaces/IEulerSwapFactory.sol

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,6 @@ pragma solidity >=0.8.0;
44
import {IEulerSwap} from "./IEulerSwap.sol";
55

66
interface IEulerSwapFactory {
7-
struct EulerAccountState {
8-
address pool;
9-
uint48 allPoolsIndex;
10-
uint48 poolMapIndex;
11-
}
12-
137
/// @notice Deploy a new EulerSwap pool with the given parameters
148
/// @dev The pool address is deterministically generated using CREATE2 with a salt derived from
159
/// the euler account address and provided salt parameter. This allows the pool address to be

0 commit comments

Comments
 (0)