Skip to content

Commit b41bca1

Browse files
0xClandestineypatil12
authored andcommitted
feat: split AllocationManager (#1643)
**Motivation:** The `AllocationManager` contract was hitting the 24KB bytecode size limit, which would have blocked deployment. We needed a solution to reduce the contract size while maintaining backwards compatibility with existing integrations that call view functions on `AllocationManager`. **Modifications:** - Created a new `SplitContractMixin` that uses a fallback to delegate unmatched function calls via `delegatecall` to a secondary contract - Split `AllocationManager` into two contracts: the main contract handles state-mutating operations, while `AllocationManagerView` handles all read-only view functions - Both contracts inherit from `AllocationManagerStorage` to share the same storage layout, enabling the view contract to read from the main contract's storage - Updated `AllocationManager` constructor to accept and store the `AllocationManagerView` address - Modified all deployment scripts (devnet, local, integration) to deploy both proxies and implementations - Updated `ExistingDeploymentParser`, test harnesses, and unit/integration tests to work with the split architecture **Result:** - The `AllocationManager` is now about ~ 4.8KB under the 24KB limit with room to grow. - The `AllocationManagerView` contract has 16KB of available space for future view functions. TODO: Add a ci check (I don't have privs but the code is written locally) 1) Get list of all contracts ending in View.sol in the repo. 2) Use forge inspect abi and check all mutability fields == view|pure. 3) Basic search over the file to see if sstore or delegatecall is used on a for additional sanity. --------- Co-authored-by: eigenmikem <[email protected]> squash squash feat: split alm wip wip wip wip wip wip wip unit tests passing remove extsload wip tests passing chore: git mv storage to folder wip ci docs todo cleanup ci ci rename `secondHalf` rm extsload move storage add protocol registry to zeus squash feat: split alm wip wip wip wip wip wip wip unit tests passing remove extsload wip tests passing chore: git mv storage to folder wip ci wip wip wip wip wip remove extsload wip tests passing wip passing perf: wrap modifier logic to save codesize chore: forge fmt refactor: remove semver (#1641) **Motivation:** We want to consolidate semver logic into a single purpose built contract. **Modifications:** - Removed `SemVerMixin` import from all contracts. **Result:** More codesize savings. make fmt refactor: expand `ProtocolRegistry` to list all contracts + pause rm gas snapshots rm extsload rebase ci revert eigen changes remove stale scripts
1 parent 9a09faa commit b41bca1

File tree

89 files changed

+1367
-2385
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

89 files changed

+1367
-2385
lines changed

.github/workflows/validate-deployment-scripts.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ jobs:
4848
- name: Install Foundry
4949
uses: foundry-rs/foundry-toolchain@82dee4ba654bd2146511f85f0d013af94670c4de
5050
with:
51-
version: stable
51+
version: v1.3.5
5252

5353
# Run Forge's formatting checker to ensure consistent code style.
5454
- name: "Forge Fmt"

docs/core/AllocationManager.md

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ Libraries and Mixins:
1010

1111
| File | Notes |
1212
| -------- | -------- |
13+
| [`SplitContractMixin.sol`](../../src/contracts/mixins/SplitContractMixin.sol) | contract splitting for codesize optimization |
1314
| [`PermissionControllerMixin.sol`](../../src/contracts/mixins/PermissionControllerMixin.sol) | account delegation |
1415
| [`Deprecated_OwnableUpgradeable`](../../src/contracts/mixins/Deprecated_OwnableUpgradeable.sol) | deprecated ownable logic |
1516
| [`Pausable.sol`](../../src/contracts/permissions/Pausable.sol) | |
@@ -29,11 +30,58 @@ The `AllocationManager` manages AVS metadata registration, registration and dere
2930

3031
The `AllocationManager's` responsibilities are broken down into the following concepts:
3132

33+
* [Contract Architecture](#contract-architecture)
3234
* [AVS Metadata](#avs-metadata)
3335
* [Operator Sets](#operator-sets)
3436
* [Allocations and Slashing](#allocations-and-slashing)
3537
* [Config](#config)
3638

39+
## Contract Architecture
40+
41+
The `AllocationManager` uses a **split contract pattern** implemented via the `SplitContractMixin` to address EVM contract size limitations while maintaining full backwards compatibility.
42+
43+
```mermaid
44+
graph TD
45+
Alice --> |call| Proxy["AllocationManager Proxy"]
46+
Proxy -->|delegatecall| Logic["AllocationManager Logic"]
47+
Logic -->|_delegateView| View["AllocationManager View"]
48+
```
49+
50+
### Split Contract Pattern
51+
52+
**Main Contract (`AllocationManager`):**
53+
- Contains all state-mutating functions (actions)
54+
- Inherits from `SplitContractMixin` which provides delegation capabilities
55+
- Delegates all view function calls to the separate view contract
56+
- Maintains the same external interface as a monolithic contract
57+
58+
**View Contract (`AllocationManagerView`):**
59+
- Contains all read-only view functions
60+
- Shares the same storage layout as the main contract using `layout at 151` directive
61+
- Implements the same `IAllocationManagerView` interface
62+
63+
### Rationale
64+
65+
**Codesize Optimization:**
66+
- The EVM has a contract size limit of 24KB (24,576 bytes) for deployed contracts
67+
- Complex contracts like `AllocationManager` with extensive functionality can exceed this limit
68+
- By splitting view functions into a separate contract, the main contract stays under the size limit
69+
- This allows for more comprehensive functionality without compromising deployability
70+
71+
**Backwards Compatibility:**
72+
- The external interface remains identical to a monolithic contract
73+
- All existing integrations continue to work without modification
74+
- View functions are transparently delegated using `_delegateView()`
75+
- No breaking changes to the ABI or function signatures
76+
77+
**Implementation Details:**
78+
- View functions in the main contract use `_delegateView(viewImplementation)` to delegate calls.
79+
- The `viewImplementation` address is set during construction and stored as an immutable variable.
80+
- The `_delegateView()` function conveniently indicates which calls are intended to be delegated as view functions, but it does not enforce this at the EVM level; rather, it signals intended usage and expected behavior to the user or integrator.
81+
- Both contracts are aligned in storage layout, so all state variables are accessible as intended.
82+
83+
This pattern is especially useful for complex contracts that require a comprehensive set of view functions while maintaining the ability to perform state mutations. It helps keep contracts deployable within EVM bytecode limits, while making clear which functions are for data retrieval.
84+
3785
## Parameterization
3886

3987
* `ALLOCATION_CONFIGURATION_DELAY`: The delay in blocks before allocations take effect.

foundry.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
"forge-std/=lib/forge-std/src/"
2323
]
2424
# Specifies the exact version of Solidity to use, overriding auto-detection.
25-
solc_version = '0.8.27'
25+
solc_version = '0.8.30'
2626
# If enabled, treats Solidity compiler warnings as errors, preventing artifact generation if warnings are present.
2727
deny_warnings = true
2828
# If set to true, changes compilation pipeline to go through the new IR optimizer.
@@ -55,7 +55,8 @@
5555
]
5656
# An array of file paths from which warnings should be ignored during compilation.
5757
ignored_warnings_from = [
58-
"src/test"
58+
"src/test",
59+
"src/contracts/core/AllocationManager.sol" # TODO: Remove
5960
]
6061

6162
# Test Configuration

script/deploy/devnet/deploy_from_scratch.s.sol

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import "../../../src/contracts/core/DelegationManager.sol";
1313
import "../../../src/contracts/core/AVSDirectory.sol";
1414
import "../../../src/contracts/core/RewardsCoordinator.sol";
1515
import "../../../src/contracts/core/AllocationManager.sol";
16+
import "../../../src/contracts/core/AllocationManagerView.sol";
1617
import "../../../src/contracts/permissions/PermissionController.sol";
1718
import "../../../src/contracts/strategies/StrategyBaseTVLLimits.sol";
1819
import "../../../src/contracts/strategies/StrategyFactory.sol";
@@ -60,6 +61,7 @@ contract DeployFromScratch is Script, Test {
6061
StrategyBase public baseStrategyImplementation;
6162
AllocationManager public allocationManagerImplementation;
6263
AllocationManager public allocationManager;
64+
AllocationManagerView public allocationManagerView;
6365
PermissionController public permissionController;
6466
PermissionController public permissionControllerImplementation;
6567

@@ -211,6 +213,9 @@ contract DeployFromScratch is Script, Test {
211213
strategyFactory = StrategyFactory(
212214
address(new TransparentUpgradeableProxy(address(emptyContract), address(eigenLayerProxyAdmin), ""))
213215
);
216+
allocationManagerView = AllocationManagerView(
217+
address(new TransparentUpgradeableProxy(address(emptyContract), address(eigenLayerProxyAdmin), ""))
218+
);
214219
permissionController = PermissionController(
215220
address(new TransparentUpgradeableProxy(address(emptyContract), address(eigenLayerProxyAdmin), ""))
216221
);
@@ -219,7 +224,7 @@ contract DeployFromScratch is Script, Test {
219224
if (chainId == 1) ethPOSDeposit = IETHPOSDeposit(0x00000000219ab540356cBB839Cbe05303d7705Fa);
220225
// if not on mainnet, deploy a mock
221226
else ethPOSDeposit = IETHPOSDeposit(stdJson.readAddress(config_data, ".ethPOSDepositAddress"));
222-
eigenPodImplementation = new EigenPod(ethPOSDeposit, eigenPodManager, SEMVER);
227+
eigenPodImplementation = new EigenPod(ethPOSDeposit, eigenPodManager);
223228

224229
eigenPodBeacon = new UpgradeableBeacon(address(eigenPodImplementation));
225230

@@ -228,43 +233,44 @@ contract DeployFromScratch is Script, Test {
228233
delegationImplementation = new DelegationManager(
229234
strategyManager,
230235
eigenPodManager,
231-
allocationManager,
236+
IAllocationManager(address(allocationManager)),
232237
eigenLayerPauserReg,
233238
permissionController,
234239
MIN_WITHDRAWAL_DELAY,
235240
SEMVER
236241
);
237242

238-
strategyManagerImplementation = new StrategyManager(allocationManager, delegation, eigenLayerPauserReg, SEMVER);
243+
strategyManagerImplementation =
244+
new StrategyManager(IAllocationManager(address(allocationManager)), delegation, eigenLayerPauserReg, SEMVER);
239245
avsDirectoryImplementation = new AVSDirectory(delegation, eigenLayerPauserReg, SEMVER);
240246
eigenPodManagerImplementation =
241-
new EigenPodManager(ethPOSDeposit, eigenPodBeacon, delegation, eigenLayerPauserReg, SEMVER);
247+
new EigenPodManager(ethPOSDeposit, eigenPodBeacon, delegation, eigenLayerPauserReg);
242248
rewardsCoordinatorImplementation = new RewardsCoordinator(
243249
IRewardsCoordinatorTypes.RewardsCoordinatorConstructorParams(
244250
delegation,
245251
strategyManager,
246-
allocationManager,
252+
IAllocationManager(address(allocationManager)),
247253
eigenLayerPauserReg,
248254
permissionController,
249255
REWARDS_COORDINATOR_CALCULATION_INTERVAL_SECONDS,
250256
REWARDS_COORDINATOR_MAX_REWARDS_DURATION,
251257
REWARDS_COORDINATOR_MAX_RETROACTIVE_LENGTH,
252258
REWARDS_COORDINATOR_MAX_FUTURE_LENGTH,
253-
REWARDS_COORDINATOR_GENESIS_REWARDS_TIMESTAMP,
254-
SEMVER
259+
REWARDS_COORDINATOR_GENESIS_REWARDS_TIMESTAMP
255260
)
256261
);
257262
allocationManagerImplementation = new AllocationManager(
263+
allocationManagerView,
258264
delegation,
259265
eigenStrategy,
260266
eigenLayerPauserReg,
261267
permissionController,
262268
DEALLOCATION_DELAY,
263-
ALLOCATION_CONFIGURATION_DELAY,
264-
SEMVER
269+
ALLOCATION_CONFIGURATION_DELAY
265270
);
266-
permissionControllerImplementation = new PermissionController(SEMVER);
267-
strategyFactoryImplementation = new StrategyFactory(strategyManager, eigenLayerPauserReg, SEMVER);
271+
272+
permissionControllerImplementation = new PermissionController();
273+
strategyFactoryImplementation = new StrategyFactory(strategyManager, eigenLayerPauserReg);
268274

269275
// Third, upgrade the proxy contracts to use the correct implementation contracts and initialize them.
270276
{
@@ -326,7 +332,7 @@ contract DeployFromScratch is Script, Test {
326332

327333
// Deploy strategyFactory & base
328334
// Create base strategy implementation
329-
baseStrategyImplementation = new StrategyBase(strategyManager, eigenLayerPauserReg, SEMVER);
335+
baseStrategyImplementation = new StrategyBase(strategyManager, eigenLayerPauserReg);
330336

331337
// Create a proxy beacon for base strategy implementation
332338
strategyBeacon = new UpgradeableBeacon(address(baseStrategyImplementation));
@@ -480,7 +486,7 @@ contract DeployFromScratch is Script, Test {
480486
"rewardsCoordinator: strategyManager address not set correctly"
481487
);
482488
require(
483-
delegationContract.allocationManager() == allocationManager,
489+
delegationContract.allocationManager() == IAllocationManager(address(allocationManager)),
484490
"delegationManager: allocationManager address not set correctly"
485491
);
486492
require(

script/deploy/local/deploy_from_scratch.slashing.s.sol

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import "../../../src/contracts/core/DelegationManager.sol";
1414
import "../../../src/contracts/core/AVSDirectory.sol";
1515
import "../../../src/contracts/core/RewardsCoordinator.sol";
1616
import "../../../src/contracts/core/AllocationManager.sol";
17+
import "../../../src/contracts/core/AllocationManagerView.sol";
1718
import "../../../src/contracts/permissions/PermissionController.sol";
1819

1920
import "../../../src/contracts/strategies/StrategyBaseTVLLimits.sol";
@@ -65,6 +66,7 @@ contract DeployFromScratch is Script, Test {
6566
StrategyBase public baseStrategyImplementation;
6667
AllocationManager public allocationManagerImplementation;
6768
AllocationManager public allocationManager;
69+
AllocationManagerView public allocationManagerView;
6870
PermissionController public permissionControllerImplementation;
6971
PermissionController public permissionController;
7072

@@ -219,17 +221,20 @@ contract DeployFromScratch is Script, Test {
219221
allocationManager = AllocationManager(
220222
address(new TransparentUpgradeableProxy(address(emptyContract), address(eigenLayerProxyAdmin), ""))
221223
);
224+
allocationManagerView = AllocationManagerView(
225+
address(new TransparentUpgradeableProxy(address(emptyContract), address(eigenLayerProxyAdmin), ""))
226+
);
222227
permissionController = PermissionController(
223228
address(new TransparentUpgradeableProxy(address(emptyContract), address(eigenLayerProxyAdmin), ""))
224229
);
225230

226-
eigenStrategy = IStrategy(new EigenStrategy(strategyManager, eigenLayerPauserReg, SEMVER));
231+
eigenStrategy = IStrategy(new EigenStrategy(strategyManager, eigenLayerPauserReg));
227232

228233
// if on mainnet, use the ETH2 deposit contract address
229234
if (chainId == 1) ethPOSDeposit = IETHPOSDeposit(0x00000000219ab540356cBB839Cbe05303d7705Fa);
230235
// if not on mainnet, deploy a mock
231236
else ethPOSDeposit = IETHPOSDeposit(stdJson.readAddress(config_data, ".ethPOSDepositAddress"));
232-
eigenPodImplementation = new EigenPod(ethPOSDeposit, eigenPodManager, SEMVER);
237+
eigenPodImplementation = new EigenPod(ethPOSDeposit, eigenPodManager);
233238

234239
eigenPodBeacon = new UpgradeableBeacon(address(eigenPodImplementation));
235240

@@ -238,41 +243,42 @@ contract DeployFromScratch is Script, Test {
238243
delegationImplementation = new DelegationManager(
239244
strategyManager,
240245
eigenPodManager,
241-
allocationManager,
246+
IAllocationManager(address(allocationManager)),
242247
eigenLayerPauserReg,
243248
permissionController,
244249
MIN_WITHDRAWAL_DELAY,
245250
SEMVER
246251
);
247-
strategyManagerImplementation = new StrategyManager(allocationManager, delegation, eigenLayerPauserReg, SEMVER);
252+
strategyManagerImplementation =
253+
new StrategyManager(IAllocationManager(address(allocationManager)), delegation, eigenLayerPauserReg, SEMVER);
248254
avsDirectoryImplementation = new AVSDirectory(delegation, eigenLayerPauserReg, SEMVER);
249255
eigenPodManagerImplementation =
250-
new EigenPodManager(ethPOSDeposit, eigenPodBeacon, delegation, eigenLayerPauserReg, SEMVER);
256+
new EigenPodManager(ethPOSDeposit, eigenPodBeacon, delegation, eigenLayerPauserReg);
251257
rewardsCoordinatorImplementation = new RewardsCoordinator(
252258
IRewardsCoordinatorTypes.RewardsCoordinatorConstructorParams(
253259
delegation,
254260
strategyManager,
255-
allocationManager,
261+
IAllocationManager(address(allocationManager)),
256262
eigenLayerPauserReg,
257263
permissionController,
258264
REWARDS_COORDINATOR_CALCULATION_INTERVAL_SECONDS,
259265
REWARDS_COORDINATOR_MAX_REWARDS_DURATION,
260266
REWARDS_COORDINATOR_MAX_RETROACTIVE_LENGTH,
261267
REWARDS_COORDINATOR_MAX_FUTURE_LENGTH,
262-
REWARDS_COORDINATOR_GENESIS_REWARDS_TIMESTAMP,
263-
SEMVER
268+
REWARDS_COORDINATOR_GENESIS_REWARDS_TIMESTAMP
264269
)
265270
);
266271
allocationManagerImplementation = new AllocationManager(
272+
allocationManagerView,
267273
delegation,
268274
eigenStrategy,
269275
eigenLayerPauserReg,
270276
permissionController,
271277
DEALLOCATION_DELAY,
272-
ALLOCATION_CONFIGURATION_DELAY,
273-
SEMVER
278+
ALLOCATION_CONFIGURATION_DELAY
274279
);
275-
permissionControllerImplementation = new PermissionController(SEMVER);
280+
281+
permissionControllerImplementation = new PermissionController();
276282

277283
// Third, upgrade the proxy contracts to use the correct implementation contracts and initialize them.
278284
{
@@ -340,7 +346,7 @@ contract DeployFromScratch is Script, Test {
340346
);
341347

342348
// deploy StrategyBaseTVLLimits contract implementation
343-
baseStrategyImplementation = new StrategyBaseTVLLimits(strategyManager, eigenLayerPauserReg, SEMVER);
349+
baseStrategyImplementation = new StrategyBaseTVLLimits(strategyManager, eigenLayerPauserReg);
344350
// create upgradeable proxies that each point to the implementation and initialize them
345351
for (uint256 i = 0; i < strategyConfigs.length; ++i) {
346352
if (strategyConfigs[i].tokenAddress == address(0)) {

script/releases/Env.sol

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,14 @@ import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.so
1111

1212
/// core/
1313
import "src/contracts/core/AllocationManager.sol";
14+
import "src/contracts/core/AllocationManagerView.sol";
1415
import "src/contracts/core/AVSDirectory.sol";
1516
import "src/contracts/core/DelegationManager.sol";
1617
import "src/contracts/core/RewardsCoordinator.sol";
1718
import "src/contracts/interfaces/IRewardsCoordinator.sol";
1819
import "src/contracts/core/StrategyManager.sol";
1920
import "src/contracts/core/ReleaseManager.sol";
21+
import "src/contracts/core/ProtocolRegistry.sol";
2022

2123
/// permissions/
2224
import "src/contracts/permissions/PauserRegistry.sol";
@@ -206,14 +208,20 @@ library Env {
206208
*/
207209
function allocationManager(
208210
DeployedProxy
209-
) internal view returns (AllocationManager) {
210-
return AllocationManager(_deployedProxy(type(AllocationManager).name));
211+
) internal view returns (IAllocationManager) {
212+
return IAllocationManager(_deployedProxy(type(AllocationManager).name));
211213
}
212214

213215
function allocationManager(
214216
DeployedImpl
215-
) internal view returns (AllocationManager) {
216-
return AllocationManager(_deployedImpl(type(AllocationManager).name));
217+
) internal view returns (IAllocationManager) {
218+
return IAllocationManager(_deployedImpl(type(AllocationManager).name));
219+
}
220+
221+
function allocationManagerView(
222+
DeployedImpl
223+
) internal view returns (IAllocationManagerView) {
224+
return IAllocationManagerView(_deployedImpl(type(AllocationManagerView).name));
217225
}
218226

219227
function avsDirectory(
@@ -276,6 +284,18 @@ library Env {
276284
return ReleaseManager(_deployedImpl(type(ReleaseManager).name));
277285
}
278286

287+
function protocolRegistry(
288+
DeployedProxy
289+
) internal view returns (ProtocolRegistry) {
290+
return ProtocolRegistry(_deployedProxy(type(ProtocolRegistry).name));
291+
}
292+
293+
function protocolRegistry(
294+
DeployedImpl
295+
) internal view returns (ProtocolRegistry) {
296+
return ProtocolRegistry(_deployedImpl(type(ProtocolRegistry).name));
297+
}
298+
279299
/**
280300
* permissions/
281301
*/

0 commit comments

Comments
 (0)