Skip to content

Commit 82a9ddd

Browse files
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]>
1 parent 9a09faa commit 82a9ddd

37 files changed

+1227
-403
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: 1 addition & 1 deletion
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.

script/deploy/devnet/deploy_from_scratch.s.sol

Lines changed: 11 additions & 4 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
);
@@ -228,22 +233,23 @@ 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 =
241247
new EigenPodManager(ethPOSDeposit, eigenPodBeacon, delegation, eigenLayerPauserReg, SEMVER);
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,
@@ -255,6 +261,7 @@ contract DeployFromScratch is Script, Test {
255261
)
256262
);
257263
allocationManagerImplementation = new AllocationManager(
264+
allocationManagerView,
258265
delegation,
259266
eigenStrategy,
260267
eigenLayerPauserReg,
@@ -480,7 +487,7 @@ contract DeployFromScratch is Script, Test {
480487
"rewardsCoordinator: strategyManager address not set correctly"
481488
);
482489
require(
483-
delegationContract.allocationManager() == allocationManager,
490+
delegationContract.allocationManager() == IAllocationManager(address(allocationManager)),
484491
"delegationManager: allocationManager address not set correctly"
485492
);
486493
require(

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

Lines changed: 10 additions & 3 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,6 +221,9 @@ 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
);
@@ -238,21 +243,22 @@ 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 =
250256
new EigenPodManager(ethPOSDeposit, eigenPodBeacon, delegation, eigenLayerPauserReg, SEMVER);
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,
@@ -264,6 +270,7 @@ contract DeployFromScratch is Script, Test {
264270
)
265271
);
266272
allocationManagerImplementation = new AllocationManager(
273+
allocationManagerView,
267274
delegation,
268275
eigenStrategy,
269276
eigenLayerPauserReg,

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
*/

script/releases/v1.7.0-v1.8.0-multichain-hourglass-combined/1-deploySourceChain.s.sol

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ contract DeploySourceChain is EOADeployer {
2525
deployedTo: address(
2626
new KeyRegistrar({
2727
_permissionController: Env.proxy.permissionController(),
28-
_allocationManager: Env.proxy.allocationManager(),
28+
_allocationManager: IAllocationManager(address(Env.proxy.allocationManager())),
2929
_version: Env.deployVersion()
3030
})
3131
)
@@ -48,7 +48,7 @@ contract DeploySourceChain is EOADeployer {
4848
name: type(CrossChainRegistry).name,
4949
deployedTo: address(
5050
new CrossChainRegistry({
51-
_allocationManager: Env.proxy.allocationManager(),
51+
_allocationManager: IAllocationManager(address(Env.proxy.allocationManager())),
5252
_keyRegistrar: Env.proxy.keyRegistrar(),
5353
_permissionController: Env.proxy.permissionController(),
5454
_pauserRegistry: Env.impl.pauserRegistry(),

script/tasks/complete_withdrawal_from_strategy.s.sol

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,8 @@ contract CompleteWithdrawFromStrategy is Script, Test {
7575
DepositScalingFactor memory dsf = DepositScalingFactor(dm.depositScalingFactor(msg.sender, strategies[0]));
7676

7777
// Get TM for Operator in strategies
78-
uint64[] memory maxMagnitudes = am.getMaxMagnitudesAtBlock(msg.sender, strategies, startBlock);
78+
uint64[] memory maxMagnitudes =
79+
IAllocationManager(address(am)).getMaxMagnitudesAtBlock(msg.sender, strategies, startBlock);
7980
uint256 slashingFactor = _getSlashingFactor(em, msg.sender, strategies[0], maxMagnitudes[0]);
8081
uint256 sharesToWithdraw = dsf.calcWithdrawable(amount, slashingFactor);
8182

script/utils/ExistingDeploymentParser.sol

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import "../../src/contracts/core/DelegationManager.sol";
1010
import "../../src/contracts/core/AVSDirectory.sol";
1111
import "../../src/contracts/core/RewardsCoordinator.sol";
1212
import "../../src/contracts/core/AllocationManager.sol";
13+
import "../../src/contracts/core/AllocationManagerView.sol";
1314
import "../../src/contracts/permissions/PermissionController.sol";
1415

1516
import "../../src/contracts/strategies/StrategyFactory.sol";
@@ -104,8 +105,9 @@ contract ExistingDeploymentParser is Script, Logger {
104105
UpgradeableBeacon public strategyBeacon;
105106

106107
/// @dev AllocationManager
107-
AllocationManager public allocationManager;
108-
AllocationManager public allocationManagerImplementation;
108+
IAllocationManager public allocationManager;
109+
IAllocationManager public allocationManagerImplementation;
110+
IAllocationManagerView public allocationManagerView;
109111

110112
/// @dev AVSDirectory
111113
AVSDirectory public avsDirectory;
@@ -230,9 +232,16 @@ contract ExistingDeploymentParser is Script, Logger {
230232
);
231233

232234
// AllocationManager
233-
allocationManager = AllocationManager(json.readAddress(".addresses.allocationManager"));
235+
allocationManager = IAllocationManager(json.readAddress(".addresses.allocationManager"));
234236
allocationManagerImplementation =
235-
AllocationManager(json.readAddress(".addresses.allocationManagerImplementation"));
237+
IAllocationManager(json.readAddress(".addresses.allocationManagerImplementation"));
238+
239+
// allocationManagerView = IAllocationManagerView(json.readAddress(".addresses.allocationManagerView"));
240+
241+
// FIXME: hotfix - remove later...
242+
allocationManagerView = new AllocationManagerView(
243+
delegationManager, eigenStrategy, DEALLOCATION_DELAY, ALLOCATION_CONFIGURATION_DELAY
244+
);
236245

237246
// AVSDirectory
238247
avsDirectory = AVSDirectory(json.readAddress(".addresses.avsDirectory"));

src/contracts/core/AVSDirectory.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import "@openzeppelin-upgrades/contracts/security/ReentrancyGuardUpgradeable.sol
77

88
import "../mixins/SignatureUtilsMixin.sol";
99
import "../permissions/Pausable.sol";
10-
import "./AVSDirectoryStorage.sol";
10+
import "./storage/AVSDirectoryStorage.sol";
1111

1212
contract AVSDirectory is
1313
Initializable,

0 commit comments

Comments
 (0)