Skip to content

Commit 19a4954

Browse files
committed
feat: meta operator registry
1 parent 17afb62 commit 19a4954

35 files changed

+1142
-462
lines changed

AGENTS.md

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@
66
- `test/`: Forge tests (`*.t.sol`), plus `test/fork/*` for fork/integration and deployment tests.
77
- `script/`: Forge scripts (deploy/verify, per-chain variants).
88
- `artifacts/`, `broadcast/`, `out/`, `cache/`: build, deploy, and fork outputs.
9-
- `lib/`, `node_modules/`: dependencies; see `remappings.txt`.
10-
- `docs/`, `digest/`: documentation and design notes.
9+
- `gists/`: small code examples related to the module.
10+
- `node_modules/`: dependencies; see `remappings.txt`.
11+
- `docs/`: documentation and design notes.
1112

1213
## Build, Test, and Development Commands
1314

@@ -18,21 +19,31 @@
1819
- `just test-local`: spins up anvil fork, deploys, runs deployment+integration tests.
1920
- `just coverage` | `just coverage-lcov`: coverage (LCOV saved; see `lcov.html`).
2021
- Linting: `yarn lint:check` (prettier + solhint), `yarn lint:fix`, `yarn lint:solhint`.
22+
- Diff: `git diff --no-ext-diff`
2123

2224
## Coding Style & Naming Conventions
2325

2426
- Formatting: Prettier + `prettier-plugin-solidity` (Solidity `printWidth=80`, `tabWidth=4`, spaces only).
2527
- Linting: Solhint (`.solhint.json`) with `solhint:recommended` and `solhint-plugin-lido-csm`.
2628
- Versions: enforce `pragma solidity 0.8.33` (`compiler-version` rule).
2729
- Naming: contracts/libraries `CamelCase` (e.g., `CSModule`, `AssetRecovererLib`), interfaces `IName` (rule: `interface-starts-with-i`).
30+
- The if statement body should be enclosed in a block, for example: `if (cond) { do(); }`
31+
- Inline assembly should be well documented, preferably every non-trivial line with its own comment.
2832
- Conventions: prefer custom errors, calldata parameters, and struct packing (gas rules). Immutable vars styled as constants.
33+
- Keep things in one function unless composable or reusable.
34+
- Prefer short variable names where possible. Prefer same length variable names for related things.
35+
- Prefer early returns to else statements.
36+
- While refactoring keep comments added from existing implementations where applicable.
37+
- Make sure external functions in contracts and interfaces have proper natspec.
38+
- When last I looked, the year was 2026.
2939

3040
## Testing Guidelines
3141

3242
- Framework: Foundry/Forge with fuzzing (`fuzz.runs=256`).
3343
- Structure: unit tests in `test/*.t.sol`; fork suites under `test/fork/*` (deployment/integration).
3444
- Run: `just test-unit` for fast cycles; `CHAIN`/`RPC_URL` required for fork tests. Example: `export CHAIN=hoodi && export RPC_URL=<https-url>`.
3545
- Coverage: `just coverage-lcov` produces LCOV output (commit if relevant).
46+
- After making changes to the source code make sure you've either ran build command or unit tests.
3647

3748
## Commit & Pull Request Guidelines
3849

Justfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ import "curated.just"
5454
default: clean deps build test-all
5555

5656
build *args:
57-
forge build --skip test --force {{args}}
57+
forge build --skip test --skip script {{args}}
5858

5959
clean:
6060
forge clean

script/DeployBase.s.sol

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,6 @@ struct DeployParams {
9191
uint256 defaultAllowedExitDelay;
9292
uint256 defaultExitDelayFee;
9393
uint256 defaultMaxWithdrawalRequestFee;
94-
uint256 defaultDepositAllocationWeight;
9594
// VettedGate
9695
address identifiedCommunityStakersGateManager;
9796
uint256 identifiedCommunityStakersGateCurveId;
@@ -115,7 +114,6 @@ struct DeployParams {
115114
uint256 identifiedCommunityStakersGateAllowedExitDelay;
116115
uint256 identifiedCommunityStakersGateExitDelayFee;
117116
uint256 identifiedCommunityStakersGateMaxWithdrawalRequestFee;
118-
uint256 identifiedCommunityStakersGateDepositAllocationWeight;
119117
// GateSeal
120118
address gateSealFactory;
121119
address sealingCommittee;
@@ -268,9 +266,7 @@ abstract contract DeployBase is Script {
268266
defaultAllowedExitDelay: config.defaultAllowedExitDelay,
269267
defaultExitDelayFee: config.defaultExitDelayFee,
270268
defaultMaxWithdrawalRequestFee: config
271-
.defaultMaxWithdrawalRequestFee,
272-
defaultDepositAllocationWeight: config
273-
.defaultDepositAllocationWeight
269+
.defaultMaxWithdrawalRequestFee
274270
})
275271
});
276272

@@ -469,15 +465,6 @@ abstract contract DeployBase is Script {
469465
identifiedCommunityStakersGateBondCurveId,
470466
config.identifiedCommunityStakersGateMaxWithdrawalRequestFee
471467
);
472-
if (
473-
config.identifiedCommunityStakersGateDepositAllocationWeight !=
474-
0
475-
) {
476-
parametersRegistry.setDepositAllocationWeight(
477-
identifiedCommunityStakersGateBondCurveId,
478-
config.identifiedCommunityStakersGateDepositAllocationWeight
479-
);
480-
}
481468

482469
feeDistributor.initialize({
483470
admin: address(deployer),

script/curated/DeployBase.s.sol

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { Verifier } from "../../src/Verifier.sol";
1717
import { ParametersRegistry } from "../../src/ParametersRegistry.sol";
1818
import { ExitPenalties } from "../../src/ExitPenalties.sol";
1919
import { OperatorsData } from "../../src/OperatorsData.sol";
20+
import { MetaOperatorRegistry } from "../../src/MetaOperatorRegistry.sol";
2021
import { CuratedGate } from "../../src/CuratedGate.sol";
2122
import { CuratedGateFactory } from "../../src/CuratedGateFactory.sol";
2223

@@ -51,7 +52,6 @@ struct GateCurveParams {
5152
uint256 allowedExitDelay;
5253
uint256 exitDelayFee;
5354
uint256 maxWithdrawalRequestFee;
54-
uint256 depositAllocationWeight;
5555
}
5656

5757
struct CuratedGateConfig {
@@ -64,6 +64,7 @@ struct CuratedGateConfig {
6464
struct CuratedDeployParams {
6565
// Lido addresses
6666
address lidoLocatorAddress;
67+
address norAddress;
6768
address aragonAgent;
6869
address easyTrackEVMScriptExecutor;
6970
address proxyAdmin;
@@ -115,8 +116,6 @@ struct CuratedDeployParams {
115116
uint256 defaultAllowedExitDelay;
116117
uint256 defaultExitDelayFee;
117118
uint256 defaultMaxWithdrawalRequestFee;
118-
uint256 defaultDepositAllocationWeight;
119-
uint256 identifiedCommunityStakersGateDepositAllocationWeight;
120119
// Curated gates
121120
CuratedGateConfig[] curatedGates;
122121
// GateSeal
@@ -149,6 +148,7 @@ abstract contract DeployBase is Script {
149148
Verifier public verifier;
150149
HashConsensus public hashConsensus;
151150
ParametersRegistry public parametersRegistry;
151+
MetaOperatorRegistry public metaOperatorsRegistry;
152152
OperatorsData public operatorsData;
153153
CuratedGateFactory public curatedGateFactory;
154154
address[] public curatedGateInstances;
@@ -219,6 +219,18 @@ abstract contract DeployBase is Script {
219219

220220
accounting = Accounting(_deployProxy(deployer, address(dummyImpl)));
221221

222+
MetaOperatorRegistry metaOperatorsRegistryImpl = new MetaOperatorRegistry(
223+
address(curatedModule),
224+
config.norAddress
225+
);
226+
metaOperatorsRegistry = MetaOperatorRegistry(
227+
_deployProxy(
228+
config.proxyAdmin,
229+
address(metaOperatorsRegistryImpl)
230+
)
231+
);
232+
metaOperatorsRegistry.initialize(deployer);
233+
222234
oracle = FeeOracle(_deployProxy(deployer, address(dummyImpl)));
223235

224236
FeeDistributor feeDistributorImpl = new FeeDistributor({
@@ -277,9 +289,7 @@ abstract contract DeployBase is Script {
277289
defaultAllowedExitDelay: config.defaultAllowedExitDelay,
278290
defaultExitDelayFee: config.defaultExitDelayFee,
279291
defaultMaxWithdrawalRequestFee: config
280-
.defaultMaxWithdrawalRequestFee,
281-
defaultDepositAllocationWeight: config
282-
.defaultDepositAllocationWeight
292+
.defaultMaxWithdrawalRequestFee
283293
})
284294
});
285295

@@ -386,12 +396,6 @@ abstract contract DeployBase is Script {
386396
curveId,
387397
params.maxWithdrawalRequestFee
388398
);
389-
if (params.depositAllocationWeight != 0) {
390-
parametersRegistry.setDepositAllocationWeight(
391-
curveId,
392-
params.depositAllocationWeight
393-
);
394-
}
395399
}
396400
accounting.revokeRole(
397401
accounting.MANAGE_BOND_CURVES_ROLE(),
@@ -407,7 +411,8 @@ abstract contract DeployBase is Script {
407411
lidoLocator: config.lidoLocatorAddress,
408412
parametersRegistry: address(parametersRegistry),
409413
accounting: address(accounting),
410-
exitPenalties: address(exitPenalties)
414+
exitPenalties: address(exitPenalties),
415+
metaOperatorsRegistry: address(metaOperatorsRegistry)
411416
});
412417

413418
{
@@ -687,6 +692,14 @@ abstract contract DeployBase is Script {
687692
deployJson.set("ChainId", chainId);
688693
deployJson.set("CuratedModule", address(curatedModule));
689694
deployJson.set("CuratedModuleImpl", address(curatedModuleImpl));
695+
deployJson.set(
696+
"MetaOperatorRegistry",
697+
address(metaOperatorsRegistry)
698+
);
699+
deployJson.set(
700+
"MetaOperatorRegistryImpl",
701+
address(metaOperatorsRegistryImpl)
702+
);
690703
deployJson.set("ParametersRegistry", address(parametersRegistry));
691704
deployJson.set(
692705
"ParametersRegistryImpl",

script/curated/DeployHoodi.s.sol

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ contract DeployHoodi is DeployBase {
1010
constructor() DeployBase("hoodi", 560048) {
1111
// Lido addresses
1212
config.lidoLocatorAddress = 0xe2EF9536DAAAEBFf5b1c130957AB3E80056b06D8;
13+
config.norAddress = 0x5cDbE1590c083b5A2A64427fAA63A7cfDB91FbB5;
1314
config.aragonAgent = 0x0534aA41907c9631fae990960bCC72d75fA7cfeD;
1415
config
1516
.easyTrackEVMScriptExecutor = 0x79a20FD0FA36453B2F45eAbab19bfef43575Ba9E;

script/curated/DeployLocalDevNet.s.sol

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ contract DeployLocalDevNet is DeployBase {
1010
constructor() DeployBase("local-devnet", vm.envUint("DEVNET_CHAIN_ID")) {
1111
// Lido addresses
1212
config.lidoLocatorAddress = vm.envAddress("CSM_LOCATOR_ADDRESS");
13+
config.norAddress = vm.envAddress("CSM_NOR_ADDRESS");
1314
config.aragonAgent = vm.envAddress("CSM_ARAGON_AGENT_ADDRESS");
1415
config.easyTrackEVMScriptExecutor = vm.envAddress(
1516
"EVM_SCRIPT_EXECUTOR_ADDRESS"

script/curated/DeployMainnet.s.sol

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ contract DeployMainnet is DeployBase {
1010
constructor() DeployBase("mainnet", 1) {
1111
// Lido addresses
1212
config.lidoLocatorAddress = 0xC1d0b3DE6792Bf6b4b37EccdcC24e45978Cfd2Eb;
13+
config.norAddress = 0x55032650b14df07b85bF18A3a3eC8E0Af2e028d5;
1314
config.aragonAgent = 0x3e40D73EB977Dc6a537aF587D48316feE66E9C8c;
1415
config
1516
.easyTrackEVMScriptExecutor = 0xFE5986E06210aC1eCC1aDCafc0cc7f8D63B3F977;

script/fork-helpers/SimulateVote.s.sol

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -195,14 +195,6 @@ contract SimulateVote is Script, ForkHelpersCommon {
195195
);
196196
vm.stopBroadcast();
197197
}
198-
{
199-
vm.startBroadcast(parametersRegistryAdmin);
200-
parametersRegistry.setDefaultDepositAllocationWeight(
201-
deployParams.defaultDepositAllocationWeight
202-
);
203-
vm.stopBroadcast();
204-
}
205-
206198
{
207199
OssifiableProxy oracleProxy = OssifiableProxy(
208200
payable(deploymentConfig.oracle)

src/Accounting.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ contract Accounting is
182182
) external onlyRole(SET_BOND_CURVE_ROLE) {
183183
_onlyExistingNodeOperator(nodeOperatorId);
184184
BondCurve._setBondCurve(nodeOperatorId, curveId);
185-
MODULE.updateDepositableValidatorsCount(nodeOperatorId);
185+
MODULE.onNodeOperatorBondCurveUpdated(nodeOperatorId);
186186
}
187187

188188
/// @inheritdoc IAccounting

src/CSModule.sol

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { SafeCast } from "@openzeppelin/contracts/utils/math/SafeCast.sol";
99
import { BaseModule } from "./abstract/BaseModule.sol";
1010

1111
import { IStakingModule, IStakingModuleV2 } from "./interfaces/IStakingModule.sol";
12-
import { NodeOperator } from "./interfaces/IBaseModule.sol";
12+
import { IBaseModule, NodeOperator } from "./interfaces/IBaseModule.sol";
1313
import { ICSModule } from "./interfaces/ICSModule.sol";
1414

1515
import { TopUpQueueLib, TopUpQueueItem, newTopUpQueueItem } from "./lib/TopUpQueueLib.sol";
@@ -332,6 +332,16 @@ contract CSModule is ICSModule, BaseModule {
332332
}
333333
}
334334

335+
/// @inheritdoc IBaseModule
336+
function onNodeOperatorBondCurveUpdated(
337+
uint256 nodeOperatorId
338+
) external override(IBaseModule) {
339+
_updateDepositableValidatorsCount({
340+
nodeOperatorId: nodeOperatorId,
341+
incrementNonceIfUpdated: true
342+
});
343+
}
344+
335345
/// @inheritdoc IStakingModule
336346
function getStakingModuleSummary()
337347
external
@@ -401,8 +411,8 @@ contract CSModule is ICSModule, BaseModule {
401411
uint256 nodeOperatorId,
402412
uint256 newCount,
403413
bool incrementNonceIfUpdated
404-
) internal override {
405-
super._applyDepositableValidatorsCount(
414+
) internal override returns (bool changed) {
415+
changed = super._applyDepositableValidatorsCount(
406416
nodeOperatorId,
407417
newCount,
408418
incrementNonceIfUpdated

0 commit comments

Comments
 (0)