Skip to content

Update operatorset#82

Merged
da-bao-jian merged 4 commits intomainfrom
update-operatorset-id
May 13, 2025
Merged

Update operatorset#82
da-bao-jian merged 4 commits intomainfrom
update-operatorset-id

Conversation

@zsluedem
Copy link
Copy Markdown
Contributor

@zsluedem zsluedem commented May 9, 2025

Summary by CodeRabbit

  • New Features

    • Unified operator management under a new "Linglong subset" abstraction, simplifying protocol and subset handling.
    • Minimum collateral value for registration can now be configured during deployment.
  • Refactor

    • Replaced previous operator set and subnetwork ID types and logic with fixed uint32 Linglong subset IDs across all contracts and interfaces.
    • Consolidated and streamlined registration, deregistration, and operator membership queries to use Linglong subsets for both EigenLayer and Symbiotic protocols.
    • Updated event, error, and function signatures for clarity and consistency.
  • Bug Fixes

    • Improved validation and error handling for subset creation and operator membership.
  • Tests

    • Updated and expanded test coverage for new Linglong subset logic, removing legacy operator set encoding tests and adding tests for subset management.
  • Chores

    • Deployment scripts and test scripts updated to reflect new function signatures and configuration parameters.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented May 9, 2025

Walkthrough

This update unifies the operator set abstraction across the codebase by replacing protocol-specific operator set handling (EigenLayer, Symbiotic) with a single "LinglongSubset" system using uint32 IDs. Function signatures, storage, modifiers, and library logic are refactored to support this model. Deployment and test scripts are updated accordingly.

Changes

File(s) Change Summary
src/libs/OperatorSubsetLib.sol Refactored to remove bit-encoded operator set logic; introduces fixed uint32 Linglong subset IDs, new struct for subset management, and helper functions for protocol identification and membership.
src/operator-registries/TaiyiRegistryCoordinator.sol, src/storage/TaiyiRegistryCoordinatorStorage.sol Refactored to use unified LinglongSubset abstraction for operator registration, deregistration, querying, and management; modifies constructor, initializers, and view functions. Storage variable updated to LinglongSubsets.
src/interfaces/ITaiyiRegistryCoordinator.sol Refactored interface: removes protocol-specific operator set functions and enums, introduces unified LinglongSubset-based signatures, and streamlines errors and events.
src/libs/EigenLayerMiddlewareLib.sol, src/libs/SymbioticNetworkMiddlewareLib.sol Updated to use LinglongSubset logic and protocol ID helpers; replaces dynamic subnetwork logic with fixed subset IDs and direct membership checks.
src/eigenlayer-avs/EigenLayerMiddleware.sol, src/eigenlayer-avs/EigenLayerRewardsHandler.sol, src/symbiotic-network/SymbioticNetworkMiddleware.sol Updated to use LinglongSubset IDs/constants for operator set/subset management and membership checks; function signatures and internal logic adjusted.
src/slasher/LinglongSlasher.sol, src/taiyi/TaiyiInteractiveChallenger.sol, src/interfaces/ILinglongChallenger.sol, src/interfaces/ILinglongSlasher.sol, test/utils/MockChallenger.sol Operator set/subset ID parameters changed from uint96 to uint32 throughout; internal logic and function signatures updated accordingly.
script/Deployments.s.sol, script/SetupContract.s.sol, script/deploy.sh Deployment script and setup updated to accept and propagate minimum collateral as a parameter; operator set constants replaced with LinglongSubset IDs.
test/OperatorSubsetLib.t.sol Test suite rewritten to validate LinglongSubset logic, removing legacy operator set encoding/decoding tests and adding subset-based membership and management tests.
test/EigenLayerMiddleware.t.sol, test/SymbioticMiddleware.t.sol Tests updated to use LinglongSubset IDs/constants and direct membership checks, replacing old operator set logic and constants.
test/DeployTest.t.sol Deployment test updated to pass minimum collateral value to deployment script.

Sequence Diagram(s)

sequenceDiagram
    participant Deployer
    participant DeployScript
    participant RegistryCoordinator
    participant Middleware

    Deployer->>DeployScript: run(configFileName, minCollateral)
    DeployScript->>RegistryCoordinator: createLinglongSubset(subsetId, minStake)
    DeployScript->>Middleware: initialize with LinglongSubset IDs
    Middleware->>RegistryCoordinator: isOperatorInLinglongSubset(subsetId, operator)
    RegistryCoordinator-->>Middleware: returns membership status
Loading

Suggested reviewers

  • da-bao-jian
  • will-luban

Poem

In fields of code where subsets sing,
The Linglong rabbits hop and bring
A single path for all to tread,
No more confusion in their stead.
With IDs clear and logic neat,
This patch makes protocol complete!
🐇✨

Tip

⚡️ Faster reviews with caching
  • CodeRabbit now supports caching for code and dependencies, helping speed up reviews. This means quicker feedback, reduced wait times, and a smoother review experience overall. Cached data is encrypted and stored securely. This feature will be automatically enabled for all accounts on May 16th. To opt out, configure Review - Disable Cache at either the organization or repository level. If you prefer to disable all data retention across your organization, simply turn off the Data Retention setting under your Organization Settings.

Enjoy the performance boost—your workflow just got faster.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 11

🔭 Outside diff range comments (3)
src/slasher/LinglongSlasher.sol (1)

481-495: ⚠️ Potential issue

Possible silent slip-through when operator is not in any Symbiotic subset

subnetwork defaults to 0.
If the operator is not found in either SYMBIOTIC_VALIDATOR_SUBSET_ID or SYMBIOTIC_UNDERWRITER_SUBSET_ID, the loop exits with subnetwork == 0, yet execution continues, potentially slashing the wrong subset.

 for (uint256 i = 0; i < subnetworks.length; i++) {
     uint32 subsetId = subnetworks[i];
     if (
         ITaiyiRegistryCoordinator(TAIYI_REGISTRY_COORDINATOR)
-            .isOperatorInLinglongSubset(baseId, operator)
+            .isOperatorInLinglongSubset(subsetId, operator)
     ) {
-        subnetwork = subnetworks[i];
+        subnetwork = subsetId;
         break;
     }
 }
 
+if (subnetwork == 0) {
+    // Operator is not part of any recognised Symbiotic subset
+    revert OperatorNotInSet(operator, 0);
+}

Fail-fast behaviour prevents accidental use of an invalid (zero) subset ID.

src/interfaces/ITaiyiRegistryCoordinator.sol (1)

40-44: ⚠️ Potential issue

Mismatch between new subset model and type of symbioticSets

AllocatedOperatorSets.symbioticSets is still declared as uint96[], while every Symbiotic-related function now uses uint32 Linglong subset IDs.
Unless there is a legacy path that still relies on the old uint96 encoding, this type divergence will:

  1. Force unnecessary casts in the implementation.
  2. Risk silent truncation or overflow when a uint96 value > uint32.max is stored in local uint32 variables.
-        uint96[] symbioticSets; // Symbiotic operator set IDs
+        uint32[] symbioticSets; // Symbiotic operator subset IDs
src/operator-registries/TaiyiRegistryCoordinator.sol (1)

694-704: ⚠️ Potential issue

Operator can register to a non-existent subset with zero minStake

The EigenLayer registration path:

  1. Reads _linglongSubsets.getMinStake(subsetId) – returns 0 for unknown IDs.
  2. Accepts the registration if stake >= 0 (always true).
  3. Adds the operator to the subset set, which creates the subset on-the-fly.

This bypasses the admin-set minStake and defeats economic gating.

Fix by asserting the subset exists and has a configured minStake:

require(
    _linglongSubsets.isLinglongSubsetIdCreated(subsetId),
    "Subset not initialised"
);
uint256 minStake = _linglongSubsets.getMinStake(subsetId);
require(minStake > 0, "Min stake not set");
require(stakes[i] >= minStake, "Stake below minimum");
🧹 Nitpick comments (12)
src/interfaces/ILinglongSlasher.sol (1)

135-144: NatSpec comment still references “operator set ID” instead of new linglongSubsetId.

The parameter has been renamed and its type narrowed to uint32, but the descriptive text (/// @param linglongSubsetId The operator set ID) still uses the old phrase.
Updating the wording improves self-documentation and avoids confusion for integrators.

-/// @param linglongSubsetId The operator set ID
+/// @param linglongSubsetId The Linglong subset ID
test/EigenLayerMiddleware.t.sol (1)

592-599: Redundant assertion & magic constant.

baseOperatorSetId is already opSet.id; asserting it equals uint32(0) neither tests production logic nor guards against regressions— it only repeats the constant chosen in setup.
Removing the assertion (or comparing to the named constant) keeps the test intention clear.

-assertEq(baseOperatorSetId, uint32(0), "Operator set ID should match");
+assertEq(
+    baseOperatorSetId,
+    OperatorSubsetLib.EIGENLAYER_VALIDATOR_SUBSET_ID,
+    "Operator set ID should match validator subset constant"
+);
test/SymbioticMiddleware.t.sol (1)

139-146: Variable rename is fine, but unused legacy constants remain.

VALIDATOR_SUBNETWORK / UNDERWRITER_SUBNETWORK constants (lines 62-63) are no longer referenced after the switch to OperatorSubsetLib.* constants.
Keeping dead constants invites drift between specs & tests—consider deleting them to prevent accidental use.

src/libs/SymbioticNetworkMiddlewareLib.sol (1)

18-23: Duplicate import & repetitive helper – clean-up needed.

SafeCast96To32Lib is imported twice (lines 18 & 20). Solidity allows dupes but this adds noise and hints at merge-conflict remnants. Remove the extra line to keep the file tidy.

-import { SafeCast96To32Lib } from "./SafeCast96To32Lib.sol";
...
-import { SafeCast96To32Lib } from "./SafeCast96To32Lib.sol";
src/eigenlayer-avs/EigenLayerMiddleware.sol (1)

104-106: Avoid silent failure – bubble-up the membership error reason

When isOperatorInLinglongSubset returns false, you revert with a custom error that implicitly targets the validator subset.
If tomorrow we reuse the modifier for a different subset, the revert text may become misleading. Consider parameterising the error or using the return-value of isOperatorInLinglongSubset to compose an accurate revert reason.

src/symbiotic-network/SymbioticNetworkMiddleware.sol (1)

74-78: Subset guard looks good, but consider library-level reuse

The validator-subset gate mirrors the one inside EigenLayerMiddleware.
Extracting it into OperatorSubsetLib (e.g., assertValidator(address, ITaiyiRegistryCoordinator)) would keep both middleware contracts consistent and avoid drift.

src/libs/EigenLayerMiddlewareLib.sol (2)

270-272: Out-of-date inline comment

The line still claims we “Get the current operator set count from allocationManager”, but the call was removed in the refactor. Please update the comment to avoid future confusion.


79-83: Variable name no longer reflects new semantics

baseId is now a plain Linglong subset ID, not a “base-encoded” value.
Renaming to subsetId (or similar) will improve readability across the file.

Also applies to: 98-103

src/slasher/LinglongSlasher.sol (1)

380-398: Gas-heavy external self-call remains — consider internalising

_checkSlashingNotInProgress performs this.isSlashingInProgress, causing an external call to the same contract (extra 2.3k gas + re-entrancy surface).
Since the function being called is view, consider making an internal version that re-uses the storage directly:

-(bool inProgress,) = this.isSlashingInProgress(operator, linglongSubsetId, challengerContract);
+(bool inProgress,) = _isSlashingInProgress(operator, linglongSubsetId, challengerContract);

…and move the existing logic into a private helper.

test/OperatorSubsetLib.t.sol (1)

20-24: Duplicate constants risk drift from library source

The four subset-ID constants are already defined in OperatorSubsetLib; redeclaring them here introduces maintenance overhead and potential divergence.

-uint32 internal constant EIGENLAYER_VALIDATOR_SUBSET_ID = 0;
-uint32 internal constant EIGENLAYER_UNDERWRITER_SUBSET_ID = 1;
-uint32 internal constant SYMBIOTIC_VALIDATOR_SUBSET_ID = 2;
-uint32 internal constant SYMBIOTIC_UNDERWRITER_SUBSET_ID = 3;
+uint32 internal constant EIGENLAYER_VALIDATOR_SUBSET_ID =
+    OperatorSubsetLib.EIGENLAYER_VALIDATOR_SUBSET_ID;
+uint32 internal constant EIGENLAYER_UNDERWRITER_SUBSET_ID =
+    OperatorSubsetLib.EIGENLAYER_UNDERWRITER_SUBSET_ID;
+uint32 internal constant SYMBIOTIC_VALIDATOR_SUBSET_ID =
+    OperatorSubsetLib.SYMBIOTIC_VALIDATOR_SUBSET_ID;
+uint32 internal constant SYMBIOTIC_UNDERWRITER_SUBSET_ID =
+    OperatorSubsetLib.SYMBIOTIC_UNDERWRITER_SUBSET_ID;

Keeps the test suite aligned with the single source of truth.

src/interfaces/ITaiyiRegistryCoordinator.sol (1)

90-95: Guard modifiers use custom errors that do not match the check they perform

onlySymbioticSubsetId and onlyEigenLayerSubsetId revert with OnlySymbioticSubsetId() and OnlyEigenlayerSubsetId() respectively.
Because these modifiers are positive-logic checks (“require(isSymbioticProtocolID … )”) the error name should convey that an invalid ID was supplied; otherwise the revert reason is misleading to integrators and tooling that parse custom errors.
Consider renaming to InvalidSymbioticSubsetId / InvalidEigenlayerSubsetId or invert the logic and message.

[nitpick]

src/operator-registries/TaiyiRegistryCoordinator.sol (1)

360-366: Gas-heavy subset size computation

getLinglongSubsetSize fetches the whole operators array then reads .length.
Use the dedicated helper to avoid unnecessary memory allocation:

-        return _linglongSubsets.getOperatorsInLinglongSubset(linglongSubsetId).length;
+        return _linglongSubsets.getLinglongSubsetLength(linglongSubsetId);
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1f41797 and b3fafb4.

📒 Files selected for processing (21)
  • script/Deployments.s.sol (5 hunks)
  • script/SetupContract.s.sol (1 hunks)
  • script/deploy.sh (1 hunks)
  • src/eigenlayer-avs/EigenLayerMiddleware.sol (5 hunks)
  • src/eigenlayer-avs/EigenLayerRewardsHandler.sol (3 hunks)
  • src/interfaces/ILinglongChallenger.sol (1 hunks)
  • src/interfaces/ILinglongSlasher.sol (1 hunks)
  • src/interfaces/ITaiyiRegistryCoordinator.sol (9 hunks)
  • src/libs/EigenLayerMiddlewareLib.sol (4 hunks)
  • src/libs/OperatorSubsetLib.sol (1 hunks)
  • src/libs/SymbioticNetworkMiddlewareLib.sol (2 hunks)
  • src/operator-registries/TaiyiRegistryCoordinator.sol (25 hunks)
  • src/slasher/LinglongSlasher.sol (6 hunks)
  • src/storage/TaiyiRegistryCoordinatorStorage.sol (1 hunks)
  • src/symbiotic-network/SymbioticNetworkMiddleware.sol (3 hunks)
  • src/taiyi/TaiyiInteractiveChallenger.sol (1 hunks)
  • test/DeployTest.t.sol (1 hunks)
  • test/EigenLayerMiddleware.t.sol (3 hunks)
  • test/OperatorSubsetLib.t.sol (1 hunks)
  • test/SymbioticMiddleware.t.sol (3 hunks)
  • test/utils/MockChallenger.sol (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Foundry Project
🔇 Additional comments (15)
script/SetupContract.s.sol (2)

120-120: Updated parameter type to use new Linglong subset identifier

This change replaces the previous VALIDATOR_SUBSET_TYPE constant with the new EIGENLAYER_VALIDATOR_SUBSET_ID from the OperatorSubsetLib, aligning with the unified operator set abstraction using uint32 IDs.


123-123: Updated parameter type to use new Linglong subset identifier

This change replaces the previous UNDERWRITER_SUBSET_TYPE constant with the new EIGENLAYER_UNDERWRITER_SUBSET_ID from the OperatorSubsetLib, aligning with the unified operator set abstraction using uint32 IDs.

test/utils/MockChallenger.sol (1)

75-75: Updated parameter type from uint96 to uint32

The parameter type has been changed from uint96 to uint32 to align with the operator set refactoring that uses uint32 Linglong subset IDs instead of the previous uint96 operator set IDs. This change ensures the mock implementation is compatible with the updated interfaces.

test/DeployTest.t.sol (1)

37-37: Updated call to include required minCollateral parameter

The deploy.run() call now includes an additional 0.1 ether parameter for the minimum collateral requirement, matching the updated function signature in script/Deployments.s.sol.

src/interfaces/ILinglongChallenger.sol (1)

116-121: Type change approved: Migrated from uint96 operatorSetId to uint32 linglongSubsetId

This change is part of the broader refactor to unify operator set identifiers under the "LinglongSubset" concept with uint32 IDs. The parameter comment has been appropriately updated to reflect this change.

src/eigenlayer-avs/EigenLayerRewardsHandler.sol (3)

18-18: Approved: Added import for OperatorSubsetLib

The import provides access to the subset ID constants used in the updated function calls below.


120-120: Approved: Updated to use unified LinglongSubset system

Changed from using hardcoded getEigenLayerOperatorSetOperators(uint32(0)) to the more semantic getLinglongSubsetOperators(OperatorSubsetLib.EIGENLAYER_UNDERWRITER_SUBSET_ID), improving code clarity and maintainability.


163-163: Approved: Updated to use unified LinglongSubset system

Changed from using hardcoded getEigenLayerOperatorSetOperators(uint32(0)) to the more semantic getLinglongSubsetOperators(OperatorSubsetLib.EIGENLAYER_VALIDATOR_SUBSET_ID), ensuring consistency in the operator subset access pattern.

script/deploy.sh (1)

18-19: Approved: Updated deployment script signature and parameters

The deployment script now accepts an additional minCollateral parameter set to 0.1ether. This aligns with updated function signatures in the deployment scripts that now require explicit collateral values instead of using hardcoded defaults.

src/taiyi/TaiyiInteractiveChallenger.sol (1)

518-518: Approved: Updated parameter type to match interface changes

Changed parameter from uint96 operatorSetId to uint32 linglongSubsetId to align with the interface changes and the unified operator subset model using LinglongSubset IDs.

test/EigenLayerMiddleware.t.sol (1)

463-467: Hard-coding subset IDs may collide across multiple AVSes.

OperatorSubsetLib.EIGENLAYER_VALIDATOR_SUBSET_ID and …_UNDERWRITER_SUBSET_ID are currently 0 and 1.
If another AVS in the same AllocationManager re-uses those constants, createOperatorSet will revert (OperatorSetAlreadyExists). Consider deriving a namespace-unique ID (e.g., hashing address(this) with a constant) or letting the middleware allocate an unused ID.

test/SymbioticMiddleware.t.sol (1)

545-552:

Details

❌ Incorrect review comment

Return value of createNewSubnetwork ignored – potential mismatch.

middleware.createNewSubnetwork(...) likely returns the actual subnetwork ID assigned on-chain.
Relying on pre-defined constants and discarding the return value will silently break if the middleware ever chooses a different ID (e.g., due to an existing subset). Capture and assert instead:

- middleware.createNewSubnetwork(OperatorSubsetLib.SYMBIOTIC_VALIDATOR_SUBSET_ID, 0);
- middleware.createNewSubnetwork(
-     OperatorSubsetLib.SYMBIOTIC_UNDERWRITER_SUBSET_ID, 0
- );
-
- validatorSubnetworkId = OperatorSubsetLib.SYMBIOTIC_VALIDATOR_SUBSET_ID;
- underwriterSubnetworkId = OperatorSubsetLib.SYMBIOTIC_UNDERWRITER_SUBSET_ID;
+ validatorSubnetworkId =
+     middleware.createNewSubnetwork(
+         OperatorSubsetLib.SYMBIOTIC_VALIDATOR_SUBSET_ID,
+         0
+     );
+ underwriterSubnetworkId =
+     middleware.createNewSubnetwork(
+         OperatorSubsetLib.SYMBIOTIC_UNDERWRITER_SUBSET_ID,
+         0
+     );
+
+ // Sanity-check that the middleware respected the requested IDs
+ assertEq(
+     validatorSubnetworkId,
+     OperatorSubsetLib.SYMBIOTIC_VALIDATOR_SUBSET_ID,
+     "Validator subnetwork ID mismatch"
+ );
+ assertEq(
+     underwriterSubnetworkId,
+     OperatorSubsetLib.SYMBIOTIC_UNDERWRITER_SUBSET_ID,
+     "Underwriter subnetwork ID mismatch"
+ );

I’ll verify whether createNewSubnetwork actually returns the new subnetwork ID by locating its definition in the codebase.


🏁 Script executed:

#!/bin/bash
# Locate the createNewSubnetwork function signature
rg -n "function createNewSubnetwork" -n .

Length of output: 136


🏁 Script executed:

#!/bin/bash
# Display the createNewSubnetwork signature and return type
rg -A3 -n "function createNewSubnetwork" src/symbiotic-network/SymbioticNetworkMiddleware.sol

Length of output: 209


🏁 Script executed:

#!/bin/bash
# Display surrounding context to inspect return type of createNewSubnetwork
rg -C5 -n "function createNewSubnetwork" src/symbiotic-network/SymbioticNetworkMiddleware.sol

Length of output: 609


🏁 Script executed:

#!/bin/bash
# Show extended context around the createNewSubnetwork signature to check for a return type
rg -C8 -n "function createNewSubnetwork" src/symbiotic-network/SymbioticNetworkMiddleware.sol

Length of output: 845


No return value to capture – createNewSubnetwork doesn’t return an ID.

The suggestion to assign and assert its return value isn’t applicable: in src/symbiotic-network/SymbioticNetworkMiddleware.sol (around lines 124–130) the createNewSubnetwork function is declared without any returns (…) clause, so it doesn’t emit a value. The existing test correctly relies on the predefined constants.

Please ignore the proposed assignment/assertEq changes.

Likely an incorrect or invalid review comment.

script/Deployments.s.sol (2)

307-308: Guard against zero-collateral deployments

Passing a zero value defeats the whole purpose of enforcing minimum collateral.

require(urcMinCollateral > 0, "minCollateral must be > 0");

Place the check immediately before populating IEigenLayerMiddleware.Config.


398-400: Signature change ripple-effect

run(string,uint256) is a breaking change for every script that previously called run(string).

Please ensure:

  1. All CI scripts (.github/workflows, Makefile, bash helpers) are updated.
  2. Any README examples or docs are adjusted.
src/operator-registries/TaiyiRegistryCoordinator.sol (1)

210-218: Subset creation callable by any registered middleware, not only Symbiotic

createLinglongSubset is protected by onlyMiddleware, which permits both eigenLayerMiddleware and symbioticMiddleware.
Creating subsets is only meaningful for Symbiotic (EigenLayer subsets are fixed at 0 & 1). Allowing the EigenLayer middleware to call this function opens the door to inconsistent state or accidental clashes.

Either:

  1. Change the modifier to onlySymbioticMiddleware, or
  2. Add a runtime check inside the function.

@zsluedem zsluedem requested a review from da-bao-jian May 10, 2025 13:44
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
src/operator-registries/TaiyiRegistryCoordinator.sol (1)

780-785: Hard-coded deregistration assumes both Symbiotic subsets have been created.

If either subset 2 or 3 was never initialised, removeOperatorFromLinglongSubsets reverts, blocking deregistration entirely.

Option A: iterate over existing subsets obtained from storage.
Option B: guard each removal with isLinglongSubsetIdCreated.

🧹 Nitpick comments (1)
src/operator-registries/TaiyiRegistryCoordinator.sol (1)

713-745: Rigid subset ID requirements may limit flexibility.

The EigenLayer deregistration function has very specific requirements about the number and specific IDs of subsets.

Consider generalizing this to handle variable subsets or validating against the actual registered subsets for the operator, which would make the code more flexible for future expansion.

- require(
-     _linglongSubsetIds.length == 2,
-     "Eigenlayer deregistration requires exactly one subset"
- );
-
- require(
-     _linglongSubsetIds[0] == OperatorSubsetLib.EIGENLAYER_VALIDATOR_SUBSET_ID
-         && _linglongSubsetIds[1] == OperatorSubsetLib.EIGENLAYER_UNDERWRITER_SUBSET_ID,
-     "Invalid eigenlayer subset IDs"
- );

+ // Validate that all subset IDs are EigenLayer protocol IDs
+ for (uint256 i = 0; i < _linglongSubsetIds.length; i++) {
+     require(
+         OperatorSubsetLib.isEigenlayerProtocolID(_linglongSubsetIds[i]),
+         "Invalid eigenlayer subset ID"
+     );
+ }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro (Legacy)

📥 Commits

Reviewing files that changed from the base of the PR and between b3fafb4 and 88d0082.

📒 Files selected for processing (3)
  • script/Deployments.s.sol (6 hunks)
  • src/operator-registries/TaiyiRegistryCoordinator.sol (25 hunks)
  • src/storage/TaiyiRegistryCoordinatorStorage.sol (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • script/Deployments.s.sol
  • src/storage/TaiyiRegistryCoordinatorStorage.sol
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Foundry Project
🔇 Additional comments (11)
src/operator-registries/TaiyiRegistryCoordinator.sol (11)

48-52: Clear and improved NatSpec documentation.

The updated NatSpec comments provide a clearer description of the contract's purpose and the registries it maintains.


65-65: Successfully refactored to use LinglongSubsets.

The storage usage has been updated to use the unified OperatorSubsetLib.LinglongSubsets abstraction instead of separate operator sets, aligning with the PR's objective.


82-88: Updated middleware access control.

The onlyMiddleware modifier correctly restricts function access to registered middleware contracts, supporting the new unified approach.


90-106: Protocol-specific modifiers implement clean separation.

The new onlySymbioticSubsetId and onlyEigenLayerSubsetId modifiers properly validate the protocol type of Linglong subset IDs, enforcing type safety throughout the contract.


158-176: Registration function successfully updated to use subset IDs.

The registerOperator function has been correctly updated to handle the new LinglongSubset abstraction.


207-218: Clean subset creation implementation.

The createLinglongSubset function properly delegates to the LinglongSubsets library with appropriate access control.


268-277: Well-implemented subnet retrieval.

The getLinglongSubnets function correctly retrieves and converts all subnet IDs to the expected return type.


308-321: Clean operator membership check using new abstraction.

The isOperatorInLinglongSubset function correctly delegates to the LinglongSubsets library.


663-711: EigenLayer registration properly validates subset types.

The function correctly ensures that only EigenLayer protocol subset IDs are used and that stake requirements are met.


747-770: Symbiotic registration properly validates subset types.

The function correctly ensures that only Symbiotic protocol subset IDs are used during registration.


787-834: Stake validation adapts well to new subset model.

The _checkInitialEigenStake function has been successfully updated to work with the new LinglongSubset IDs while maintaining the same validation logic.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
src/operator-registries/TaiyiRegistryCoordinator.sol (1)

776-786: ⚠️ Potential issue

Hard-coded deregistration still assumes both Symbiotic subsets exist

The function _deregisterOperatorForSymbiotic hardcodes both Symbiotic subset IDs and attempts to remove the operator from them without checking if they exist. If either subset was never initialized, the call to removeOperatorFromLinglongSubsets will revert, blocking deregistration completely.

Modify the code to guard against non-existent subsets:

function _deregisterOperatorForSymbiotic(address operator) internal {
    OperatorInfo storage operatorInfo = _operatorInfo[operator];
    require(operatorInfo.status == OperatorStatus.REGISTERED, OperatorNotRegistered());

    uint32[] memory linglongSubsetIds = new uint32[](2);
    linglongSubsetIds[0] = OperatorSubsetLib.SYMBIOTIC_VALIDATOR_SUBSET_ID;
    linglongSubsetIds[1] = OperatorSubsetLib.SYMBIOTIC_UNDERWRITER_SUBSET_ID;

-   _linglongSubsets.removeOperatorFromLinglongSubsets(linglongSubsetIds, operator);
+   // Only remove from subsets that exist
+   if (_linglongSubsets.linglongSubsetIds.contains(OperatorSubsetLib.SYMBIOTIC_VALIDATOR_SUBSET_ID)) {
+       uint32[] memory validatorSubset = new uint32[](1);
+       validatorSubset[0] = OperatorSubsetLib.SYMBIOTIC_VALIDATOR_SUBSET_ID;
+       _linglongSubsets.removeOperatorFromLinglongSubsets(validatorSubset, operator);
+   }
+   
+   if (_linglongSubsets.linglongSubsetIds.contains(OperatorSubsetLib.SYMBIOTIC_UNDERWRITER_SUBSET_ID)) {
+       uint32[] memory underwriterSubset = new uint32[](1);
+       underwriterSubset[0] = OperatorSubsetLib.SYMBIOTIC_UNDERWRITER_SUBSET_ID;
+       _linglongSubsets.removeOperatorFromLinglongSubsets(underwriterSubset, operator);
+   }
🧹 Nitpick comments (3)
src/operator-registries/TaiyiRegistryCoordinator.sol (3)

734-749: Duplicate code for operator status update logic

The status checking logic in both _deregisterOperatorForEigenlayer and _deregisterOperatorForSymbiotic is duplicated. This makes the code harder to maintain and increases the risk of inconsistencies if one implementation is updated but not the other.

Extract the common logic into a helper function:

+// Extract common deregistration logic to a helper function
+function _updateOperatorStatusAfterRemoval(address operator) internal {
+    // Check if the operator is still in any Linglong subset
+    bool stillInAnySubset = false;
+    uint256[] memory allSubsetIds = _linglongSubsets.linglongSubsetIds.values();
+    for (uint256 i = 0; i < allSubsetIds.length; i++) {
+        uint32 subsetId = uint32(allSubsetIds[i]);
+        if (_linglongSubsets.isOperatorInLinglongSubset(subsetId, operator)) {
+            stillInAnySubset = true;
+            break;
+        }
+    }
+
+    // Only set status to DEREGISTERED if not in any subset
+    if (!stillInAnySubset) {
+        _operatorInfo[operator].status = OperatorStatus.DEREGISTERED;
+    }
+}

// In _deregisterOperatorForEigenlayer:
-// Check if the operator is still in any Linglong subset
-bool stillInAnySubset = false;
-uint256[] memory allSubsetIds = _linglongSubsets.linglongSubsetIds.values();
-for (uint256 i = 0; i < allSubsetIds.length; i++) {
-    uint32 subsetId = uint32(allSubsetIds[i]);
-    if (_linglongSubsets.isOperatorInLinglongSubset(subsetId, operator)) {
-        stillInAnySubset = true;
-        break;
-    }
-}
-
-// Only set status to DEREGISTERED if not in any subset
-if (!stillInAnySubset) {
-    operatorInfo.status = OperatorStatus.DEREGISTERED;
-}
+_updateOperatorStatusAfterRemoval(operator);

// In _deregisterOperatorForSymbiotic:
-// Check if the operator is still in any Linglong subset
-bool stillInAnySubset = false;
-uint256[] memory allSubsetIds = _linglongSubsets.linglongSubsetIds.values();
-for (uint256 i = 0; i < allSubsetIds.length; i++) {
-    uint32 subsetId = uint32(allSubsetIds[i]);
-    if (_linglongSubsets.isOperatorInLinglongSubset(subsetId, operator)) {
-        stillInAnySubset = true;
-        break;
-    }
-}
-
-// Only set status to DEREGISTERED if not in any subset
-if (!stillInAnySubset) {
-    operatorInfo.status = OperatorStatus.DEREGISTERED;
-}
+_updateOperatorStatusAfterRemoval(operator);

Also applies to: 788-803


843-843: Add comment explaining maxMag check

The logic to skip strategies with zero maximum magnitude is correct (to prevent division by zero), but lacks documentation explaining why this check is necessary.

-               if (maxMag == 0) continue;
+               // Skip strategies with zero maximum magnitude to prevent division by zero in stake calculation
+               if (maxMag == 0) continue;

268-277: Consider optimizing getLinglongSubnets implementation

The current implementation creates a new array and copies values to convert from uint256[] to uint32[]. This could be optimized for gas efficiency.

function getLinglongSubnets() external view returns (uint32[] memory) {
-   uint256[] memory subnetIds = _linglongSubsets.linglongSubsetIds.values();
-   uint32[] memory subsetIds = new uint32[](subnetIds.length);
-   for (uint256 i = 0; i < subnetIds.length; i++) {
-       subsetIds[i] = uint32(subnetIds[i]);
-   }
-   return subsetIds;
+   // Get all subset IDs
+   uint256[] memory subsetIds = _linglongSubsets.linglongSubsetIds.values();
+   
+   // Convert in place using SafeCast
+   return subsetIds.toUint32Array();
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro (Legacy)

📥 Commits

Reviewing files that changed from the base of the PR and between 88d0082 and f38fc81.

📒 Files selected for processing (1)
  • src/operator-registries/TaiyiRegistryCoordinator.sol (25 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Foundry Project
🔇 Additional comments (3)
src/operator-registries/TaiyiRegistryCoordinator.sol (3)

663-711: Registration and operator ID handling looks good

The implementation properly handles operator registration for EigenLayer, including validating subset IDs, checking stakes against minimum requirements, and registering with the middleware when needed.


401-534: Strategy allocation query functions correctly updated

The functions for querying strategy allocations have been properly updated to use Linglong subset IDs and include the appropriate protocol-specific subset ID validation modifiers.


82-106: Well-designed unified access control system

The updated modifier system effectively unifies access control while maintaining separation of concerns. The onlyMiddleware modifier handles authentication while onlySymbioticSubsetId and onlyEigenLayerSubsetId ensure proper authorization for protocol-specific operations.


// Extract the base ID by masking out the protocol bits
baseId = encodedId & ID_MASK_32;
// the below are the available linglong subset id
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not blocking the merge but just a question

how should we handle the id updates when we add more operator sets and subnetworks

@will-luban @zsluedem

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just use different incremented number. I am not sure what kind of operator set and subnetwork you have in mind for the future. Because your original implementation doesn't actually address that either, I don't actually get what your future plan for it.
Are you talking about supporting more restaking protocol or something else?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More operator set like gateway operator set, underwriter operator set. There could be one operator set for each based rollup etc

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This that case, do we need to implement some contracts for based rollup like eigenlayer middleware? These constant are actually used in the middleware.

@da-bao-jian da-bao-jian merged commit 4926aec into main May 13, 2025
2 checks passed
@da-bao-jian da-bao-jian deleted the update-operatorset-id branch May 13, 2025 09:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants