CSMSatellite

Description:

Smart contract deployed on Ethereum with Factory features.

Blockchain: Ethereum

Source Code: View Code On The Blockchain

Solidity Source Code:

{{
  "language": "Solidity",
  "sources": {
    "src/CSMSatellite.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

import "./interfaces/ICSModule.sol";
import {Batch} from "./interfaces/ICSModule.sol";

struct NodeOperatorShort {
    uint256 id;
    address managerAddress;
    address rewardAddress;
    bool extendedManagerPermissions;
}

struct NodeOperatorProposed {
    uint256 id;
    address proposedManagerAddress;
    address proposedRewardAddress;
}

enum SearchMode {
    CURRENT_ADDRESSES,
    PROPOSED_ADDRESSES,
    ALL_ADDRESSES
}

contract CSMSatellite {
    ICSModule public immutable csModule;

    constructor(address _csModuleAddress) {
        require(
            _csModuleAddress != address(0),
            "CSModule address cannot be zero"
        );
        csModule = ICSModule(_csModuleAddress);
    }

    /**
     * @notice Finds Node Operator IDs by a given address, searching within a specified range.
     * @param _addressToSearch The address to match against Node Operator addresses.
     * @param _offset The starting index (0-based) of Node Operators to check.
     * @param _limit The maximum number of Node Operator IDs to return in this call.
     * @param _searchMode The search mode determining which addresses to check (current, proposed, or all).
     * @return matchingOperatorIds An array containing the IDs of matching Node Operators found within the page.
     */
    function findNodeOperatorsByAddress(
        address _addressToSearch,
        uint256 _offset,
        uint256 _limit,
        SearchMode _searchMode
    ) external view returns (uint256[] memory) {
        require(_addressToSearch != address(0), "Address cannot be zero");
        require(_limit > 0, "Limit must be greater than zero");

        uint256 totalOperators = csModule.getNodeOperatorsCount();

        if (_offset >= totalOperators) {
            return new uint256[](0);
        }

        uint256 endIndex = _offset + _limit;
        if (endIndex > totalOperators) {
            endIndex = totalOperators;
        }

        uint256[] memory tempResults = new uint256[](_limit);
        uint256 resultCount = 0;

        for (uint256 i = _offset; i < endIndex; i++) {
            ICSModule.NodeOperator memory operator = csModule.getNodeOperator(
                i
            );

            bool matches = false;

            if (_searchMode == SearchMode.CURRENT_ADDRESSES) {
                matches = (operator.managerAddress == _addressToSearch ||
                    operator.rewardAddress == _addressToSearch);
            } else if (_searchMode == SearchMode.PROPOSED_ADDRESSES) {
                matches = (operator.proposedManagerAddress ==
                    _addressToSearch ||
                    operator.proposedRewardAddress == _addressToSearch);
            } else if (_searchMode == SearchMode.ALL_ADDRESSES) {
                matches = (operator.managerAddress == _addressToSearch ||
                    operator.rewardAddress == _addressToSearch ||
                    operator.proposedManagerAddress == _addressToSearch ||
                    operator.proposedRewardAddress == _addressToSearch);
            }

            if (matches) {
                tempResults[resultCount] = i;
                resultCount++;
            }
        }

        uint256[] memory results = new uint256[](resultCount);
        for (uint256 i = 0; i < resultCount; i++) {
            results[i] = tempResults[i];
        }

        return results;
    }

    /**
     * @notice Finds Node Operators by a given address and returns their details, searching within a specified range.
     * @dev Only searches current addresses (managerAddress and rewardAddress), not proposed addresses.
     * @param _addressToSearch The address to match against Node Operator current addresses.
     * @param _offset The starting index (0-based) of Node Operators to check.
     * @param _limit The maximum number of Node Operators to return in this call.
     * @return operators An array containing the details of matching Node Operators found within the page.
     */
    function getNodeOperatorsByAddress(
        address _addressToSearch,
        uint256 _offset,
        uint256 _limit
    ) external view returns (NodeOperatorShort[] memory) {
        require(_addressToSearch != address(0), "Address cannot be zero");
        require(_limit > 0, "Limit must be greater than zero");

        uint256 totalOperators = csModule.getNodeOperatorsCount();

        if (_offset >= totalOperators) {
            return new NodeOperatorShort[](0);
        }

        uint256 endIndex = _offset + _limit;
        if (endIndex > totalOperators) {
            endIndex = totalOperators;
        }

        NodeOperatorShort[] memory tempResults = new NodeOperatorShort[](
            _limit
        );
        uint256 resultCount = 0;

        for (uint256 i = _offset; i < endIndex; i++) {
            ICSModule.NodeOperatorManagementProperties
                memory operator = csModule.getNodeOperatorManagementProperties(
                    i
                );

            bool matches = (operator.managerAddress == _addressToSearch ||
                operator.rewardAddress == _addressToSearch);

            if (matches) {
                tempResults[resultCount] = NodeOperatorShort({
                    id: i,
                    managerAddress: operator.managerAddress,
                    rewardAddress: operator.rewardAddress,
                    extendedManagerPermissions: operator
                        .extendedManagerPermissions
                });
                resultCount++;
            }
        }

        NodeOperatorShort[] memory results = new NodeOperatorShort[](
            resultCount
        );
        for (uint256 i = 0; i < resultCount; i++) {
            results[i] = tempResults[i];
        }

        return results;
    }

    /**
     * @notice Finds Node Operators by a given address matching proposed addresses, searching within a specified range.
     * @dev Only searches proposed addresses (proposedManagerAddress and proposedRewardAddress).
     * @param _addressToSearch The address to match against Node Operator proposed addresses.
     * @param _offset The starting index (0-based) of Node Operators to check.
     * @param _limit The maximum number of Node Operators to return in this call.
     * @return operators An array containing the IDs and proposed addresses of matching Node Operators found within the page.
     */
    function getNodeOperatorsByProposedAddress(
        address _addressToSearch,
        uint256 _offset,
        uint256 _limit
    ) external view returns (NodeOperatorProposed[] memory) {
        require(_addressToSearch != address(0), "Address cannot be zero");
        require(_limit > 0, "Limit must be greater than zero");

        uint256 totalOperators = csModule.getNodeOperatorsCount();

        if (_offset >= totalOperators) {
            return new NodeOperatorProposed[](0);
        }

        uint256 endIndex = _offset + _limit;
        if (endIndex > totalOperators) {
            endIndex = totalOperators;
        }

        NodeOperatorProposed[] memory tempResults = new NodeOperatorProposed[](
            _limit
        );
        uint256 resultCount = 0;

        for (uint256 i = _offset; i < endIndex; i++) {
            ICSModule.NodeOperator memory operator = csModule.getNodeOperator(
                i
            );

            bool matches = (operator.proposedManagerAddress ==
                _addressToSearch ||
                operator.proposedRewardAddress == _addressToSearch);

            if (matches) {
                tempResults[resultCount] = NodeOperatorProposed({
                    id: i,
                    proposedManagerAddress: operator.proposedManagerAddress,
                    proposedRewardAddress: operator.proposedRewardAddress
                });
                resultCount++;
            }
        }

        NodeOperatorProposed[] memory results = new NodeOperatorProposed[](
            resultCount
        );
        for (uint256 i = 0; i < resultCount; i++) {
            results[i] = tempResults[i];
        }

        return results;
    }

    /**
     * @notice Retrieves the depositable validators count for a range of Node Operators.
     * @dev Returns an array where each element is the depositableValidatorsCount for the corresponding operator.
     *      The operator ID can be calculated as: operatorId = _offset + arrayIndex
     *
     *      Example usage:
     *      - Call with _offset=0, _limit=100 to get counts for operators 0-99
     *      - result[0] = depositableValidatorsCount for operator 0
     *      - result[50] = depositableValidatorsCount for operator 50
     *
     *      This is a lightweight, gas-efficient method to retrieve depositable validator counts
     *      for analytics and monitoring purposes.
     *
     * @param _offset The starting index (0-based) of Node Operators to query.
     * @param _limit The maximum number of operators to return counts for.
     * @return An array of uint32 values representing depositableValidatorsCount for each operator in the range.
     */
    function getNodeOperatorsDepositableValidatorsCount(
        uint256 _offset,
        uint256 _limit
    ) external view returns (uint32[] memory) {
        require(_limit > 0, "Limit must be greater than zero");

        uint256 totalOperators = csModule.getNodeOperatorsCount();

        if (_offset >= totalOperators) {
            return new uint32[](0);
        }

        uint256 endIndex = _offset + _limit;
        if (endIndex > totalOperators) {
            endIndex = totalOperators;
        }

        uint256 resultCount = endIndex - _offset;
        uint32[] memory results = new uint32[](resultCount);

        for (uint256 i = _offset; i < endIndex; i++) {
            ICSModule.NodeOperator memory operator = csModule.getNodeOperator(
                i
            );
            results[i - _offset] = operator.depositableValidatorsCount;
        }

        return results;
    }

    /**
     * @notice Retrieves deposit queue batches using linked-list traversal via batch.next() pointers.
     * @dev This function follows the queue's linked-list structure and respects the head pointer,
     *      ensuring only active batches in the queue are returned (between head and tail).
     *      It properly handles scenarios where batches have been skipped (head advanced past some indices).
     *
     *      Pagination approach:
     *      - Use cursorIndex=0 to start from the queue head
     *      - Extract next cursor from last batch: batches[batches.length - 1].next()
     *      - If returned array is empty, you've reached the end of the queue
     *      - If last batch's next() >= tail, you've reached the end
     *
     * @param _queuePriority The priority level of the queue to retrieve batches from.
     * @param _cursorIndex The batch index to start from (use 0 to start from head, or next() from previous page's last batch).
     * @param _limit The maximum number of batches to return.
     * @return batches An array of Batch structures traversed via next pointers. Extract next cursor via batches[length-1].next().
     */
    function getDepositQueueBatches(
        uint256 _queuePriority,
        uint128 _cursorIndex,
        uint256 _limit
    ) external view returns (Batch[] memory batches) {
        require(_limit > 0, "Limit must be greater than zero");
        require(
            _queuePriority <= csModule.QUEUE_LOWEST_PRIORITY(),
            "Invalid queue priority"
        );

        (uint128 head, uint128 tail) = csModule.depositQueuePointers(
            _queuePriority
        );

        // If queue is empty, return empty array
        if (head == tail) {
            return new Batch[](0);
        }

        // Validate cursor is within active queue range
        if (_cursorIndex != 0 && _cursorIndex < head) {
            revert("Cursor is behind queue head");
        }

        // Start from head if cursor is 0, otherwise use the provided cursor
        uint128 currentIndex = _cursorIndex == 0 ? head : _cursorIndex;

        // If current index is at or beyond tail, we're done
        if (currentIndex >= tail) {
            return new Batch[](0);
        }

        // Allocate temporary array with max possible size
        Batch[] memory tempBatches = new Batch[](_limit);
        uint256 count = 0;

        // Traverse the linked list using next() pointers
        while (count < _limit && currentIndex < tail) {
            Batch batch = csModule.depositQueueItem(
                _queuePriority,
                currentIndex
            );
            tempBatches[count] = batch;
            count++;

            // Get next index from batch
            uint128 nextIndex = batch.next();

            // If next points to tail or beyond, we've reached the end
            if (nextIndex >= tail) {
                break;
            }

            currentIndex = nextIndex;
        }

        // Create result array with actual size
        batches = new Batch[](count);
        for (uint256 i = 0; i < count; i++) {
            batches[i] = tempBatches[i];
        }

        return batches;
    }
}
"
    },
    "src/interfaces/ICSModule.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

// Batch is an uint256 as it's the internal data type used by solidity.
// Batch is a packed value, consisting of the following fields:
//    - uint64  nodeOperatorId
//    - uint64  keysCount -- count of keys enqueued by the batch
//    - uint128 next -- index of the next batch in the queue
type Batch is uint256;

/// @dev Syntactic sugar for the type.
function unwrap(Batch self) pure returns (uint256) {
    return Batch.unwrap(self);
}

function noId(Batch self) pure returns (uint64 n) {
    assembly {
        n := shr(192, self)
    }
}

function keys(Batch self) pure returns (uint64 n) {
    assembly {
        n := shl(64, self)
        n := shr(192, n)
    }
}

function next(Batch self) pure returns (uint128 n) {
    assembly {
        n := shl(128, self)
        n := shr(128, n)
    }
}

using {noId, keys, next, unwrap} for Batch global;

interface ICSModule {
    struct NodeOperator {
        uint32 totalAddedKeys;
        uint32 totalWithdrawnKeys;
        uint32 totalDepositedKeys;
        uint32 totalVettedKeys;
        uint32 stuckValidatorsCount;
        uint32 depositableValidatorsCount;
        uint32 targetLimit;
        uint8 targetLimitMode;
        uint32 totalExitedKeys;
        uint32 enqueuedCount;
        address managerAddress;
        address proposedManagerAddress;
        address rewardAddress;
        address proposedRewardAddress;
        bool extendedManagerPermissions;
        bool usedPriorityQueue;
    }

    struct NodeOperatorManagementProperties {
        address managerAddress;
        address rewardAddress;
        bool extendedManagerPermissions;
    }

    // type Batch is uint256;

    function getNodeOperator(
        uint256 nodeOperatorId
    ) external view returns (NodeOperator memory);

    function getNodeOperatorManagementProperties(
        uint256 nodeOperatorId
    ) external view returns (NodeOperatorManagementProperties memory);

    function getNodeOperatorsCount() external view returns (uint256);

    /// @dev QUEUE_LOWEST_PRIORITY identifies the range of available priorities: [0; QUEUE_LOWEST_PRIORITY].
    function QUEUE_LOWEST_PRIORITY() external view returns (uint256);

    function depositQueuePointers(
        uint256 queuePriority
    ) external view returns (uint128 head, uint128 tail);

    function depositQueueItem(
        uint256 queuePriority,
        uint128 index
    ) external view returns (Batch);
}
"
    }
  },
  "settings": {
    "remappings": [
      "forge-std/=lib/forge-std/src/"
    ],
    "optimizer": {
      "enabled": false,
      "runs": 200
    },
    "metadata": {
      "useLiteralContent": false,
      "bytecodeHash": "ipfs",
      "appendCBOR": true
    },
    "outputSelection": {
      "*": {
        "*": [
          "evm.bytecode",
          "evm.deployedBytecode",
          "devdoc",
          "userdoc",
          "metadata",
          "abi"
        ]
      }
    },
    "evmVersion": "cancun",
    "viaIR": false
  }
}}

Tags:
Factory|addr:0xf1199b61429e16e5c9f1a3f73a1190b52bc81ddc|verified:true|block:23588323|tx:0xe2f1b57a350d9d644d1cf94c3c4465bcdf7d66dee9df9841c4700f0072daab12|first_check:1760606129

Submitted on: 2025-10-16 11:15:32

Comments

Log in to comment.

No comments yet.