NewtonProverTaskManager

Description:

Multi-signature wallet contract requiring multiple confirmations for transaction execution.

Blockchain: Ethereum

Source Code: View Code On The Blockchain

Solidity Source Code:

{{
  "language": "Solidity",
  "sources": {
    "src/NewtonProverTaskManager.sol": {
      "content": "// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.27;

import "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol";
import "@openzeppelin-upgrades/contracts/access/OwnableUpgradeable.sol";
import "@eigenlayer/contracts/permissions/Pausable.sol";
import {ISlashingRegistryCoordinator} from
    "@eigenlayer-middleware/src/interfaces/ISlashingRegistryCoordinator.sol";
import {OperatorStateRetriever} from "@eigenlayer-middleware/src/OperatorStateRetriever.sol";
import {BLSSignatureChecker} from "@eigenlayer-middleware/src/BLSSignatureChecker.sol";
import "@eigenlayer-middleware/src/libraries/BN254.sol";
import {INewtonProverTaskManager} from "./interfaces/INewtonProverTaskManager.sol";
import {NewtonMessage} from "./core/NewtonMessage.sol";
import {TaskLib} from "./libraries/TaskLib.sol";
import {ChallengeLib} from "./libraries/ChallengeLib.sol";

contract NewtonProverTaskManager is
    Initializable,
    OwnableUpgradeable,
    Pausable,
    BLSSignatureChecker,
    OperatorStateRetriever,
    INewtonProverTaskManager
{
    using BN254 for BN254.G1Point;

    /* CONSTANT */
    uint256 internal constant _THRESHOLD_DENOMINATOR = 100;

    /* STORAGE */
    uint32 public nonce;
    TaskManagerConfig internal _taskManagerConfig;

    // Core entity addresses
    address public serviceManager;
    address public aggregator;
    address public generator;
    address public instantSlasher;
    address public allocationManager;

    // Task-related mappings
    mapping(bytes32 => bytes32) public allTaskHashes;
    mapping(bytes32 => bytes32) public allTaskResponses;
    mapping(bytes32 => bool) public taskSuccesfullyChallenged;
    mapping(bytes32 => bytes32) public attestations;

    /* MODIFIERS */
    modifier onlyAggregator() {
        require(msg.sender == aggregator, OnlyAggregator());
        _;
    }

    // onlyTaskGenerator is used to restrict createNewTask from only being called by a permissioned entity
    // in a real world scenario, this would be removed by instead making createNewTask a payable function
    modifier onlyTaskGenerator() {
        require(msg.sender == generator, OnlyTaskGenerator());
        _;
    }

    // onlyPolicyClient is used to restrict validateAttestation from only being called by a policy client
    modifier onlyPolicyClient() {
        TaskLib.onlyPolicyClient();
        _;
    }

    modifier onlyValidTaskResponse(Task calldata task, TaskResponse calldata taskResponse) {
        TaskLib.sanityCheckTaskResponse(
            task, taskResponse, uint32(block.number), _taskManagerConfig.taskResponseWindowBlock
        );
        _;
    }

    constructor(
        ISlashingRegistryCoordinator _slashingRegistryCoordinator,
        IPauserRegistry _pauserRegistry
    )
        BLSSignatureChecker(ISlashingRegistryCoordinator(_slashingRegistryCoordinator))
        Pausable(_pauserRegistry)
    {}

    function initialize(
        address initialOwner,
        address _aggregator,
        address _generator,
        address _allocationManager,
        address _slasher,
        address _serviceManager,
        TaskManagerConfig memory taskManagerConfig
    ) public initializer {
        _transferOwnership(initialOwner);
        aggregator = _aggregator;
        generator = _generator;
        allocationManager = _allocationManager;
        instantSlasher = _slasher;
        serviceManager = _serviceManager;
        _taskManagerConfig = taskManagerConfig;
    }

    /* FUNCTIONS */
    // NOTE: this function creates new task, assigns it a taskId
    function createNewTask(
        address policyClient,
        NewtonMessage.Intent calldata intent,
        NewtonMessage.PolicyTaskData calldata policyTaskData,
        bytes calldata quorumNumbers,
        uint32 quorumThresholdPercentage
    ) external onlyTaskGenerator {
        INewtonProverTaskManager.Task memory newTask = TaskLib.createTask(
            nonce, policyClient, intent, policyTaskData, quorumNumbers, quorumThresholdPercentage
        );
        allTaskHashes[newTask.taskId] = keccak256(abi.encode(newTask));

        emit NewTaskCreated(newTask.taskId, newTask);
        unchecked {
            ++nonce;
        }
    }

    // NOTE: this function returns the latest task nonce.
    function latestNonce() external view returns (uint32) {
        return nonce;
    }

    // NOTE: this function responds to existing tasks.
    function respondToTask(
        Task calldata task,
        TaskResponse calldata taskResponse,
        NonSignerStakesAndSignature memory nonSignerStakesAndSignature
    ) external onlyAggregator onlyValidTaskResponse(task, taskResponse) {
        bytes32 taskId = taskResponse.taskId;

        // Validate task and response
        require(keccak256(abi.encode(task)) == allTaskHashes[taskId], TaskLib.TaskMismatch());
        require(allTaskResponses[taskId] == bytes32(0), TaskLib.TaskAlreadyResponded());

        // Check signatures and threshold
        bytes32 message = keccak256(abi.encode(taskResponse));
        (QuorumStakeTotals memory quorumStakeTotals, bytes32 hashOfNonSigners) = checkSignatures(
            message, task.quorumNumbers, uint32(task.taskCreatedBlock), nonSignerStakesAndSignature
        );

        // Validate quorum thresholds
        uint8 threshold = uint8(task.quorumThresholdPercentage);
        for (uint256 i; i < task.quorumNumbers.length;) {
            require(
                quorumStakeTotals.signedStakeForQuorum[i] * _THRESHOLD_DENOMINATOR
                    >= quorumStakeTotals.totalStakeForQuorum[i] * threshold,
                InsufficientQuorumStake()
            );
            unchecked {
                ++i;
            }
        }

        // Create response metadata
        uint32 referenceBlock = uint32(block.number);
        uint32 responseExpireBlock = referenceBlock + task.policyConfig.expireAfter;
        ResponseCertificate memory responseCertificate = ResponseCertificate(
            referenceBlock, hashOfNonSigners, nonSignerStakesAndSignature, responseExpireBlock
        );

        // Store task response
        allTaskResponses[taskId] = keccak256(abi.encode(taskResponse, responseCertificate));

        // Create attestation if result is valid
        if (TaskLib.evaluateResult(taskResponse.evaluationResult)) {
            NewtonMessage.Attestation memory attestation = NewtonMessage.Attestation(
                taskId,
                taskResponse.policyId,
                taskResponse.policyClient,
                taskResponse.intent,
                responseExpireBlock
            );
            attestations[taskId] = keccak256(abi.encode(attestation));
        }

        emit TaskResponded(taskResponse, responseCertificate);
    }

    function raiseAndResolveChallenge(
        Task calldata task,
        TaskResponse calldata taskResponse,
        ResponseCertificate calldata responseCertificate,
        ChallengeData calldata challenge,
        BN254.G1Point[] memory pubkeysOfNonSigningOperators
    ) external {
        require(_taskManagerConfig.isChallengeEnabled, ChallengeNotEnabled());
        require(
            keccak256(abi.encode(task)) == allTaskHashes[taskResponse.taskId],
            TaskLib.TaskMismatch()
        );
        require(
            _isChallengable(task, taskResponse, responseCertificate, challenge), NotChallengable()
        );
        require(
            uint32(block.number)
                <= responseCertificate.referenceBlock + _taskManagerConfig.taskChallengeWindowBlock,
            ChallengePeriodExpired()
        );

        bytes32 taskId = taskResponse.taskId;
        bool isResponseCorrect = TaskLib.evaluateResult(challenge.data);

        if (isResponseCorrect) {
            emit TaskChallengedUnsuccessfully(taskId, msg.sender);
            return;
        }

        // Process non-signing operators and validate
        (
            bytes32[] memory hashesOfPubkeysOfNonSigningOperators,
            address[] memory addressOfNonSigningOperators
        ) = ChallengeLib.processNonSigners(pubkeysOfNonSigningOperators, address(blsApkRegistry));

        ChallengeLib.validateSignatoryRecord(
            task.taskCreatedBlock,
            hashesOfPubkeysOfNonSigningOperators,
            responseCertificate.hashOfNonSigners
        );

        // Slash signing operators
        ChallengeLib.ChallengeContext memory ctx = ChallengeLib.ChallengeContext({
            blsApkRegistry: address(blsApkRegistry),
            registryCoordinator: address(registryCoordinator),
            allocationManager: allocationManager,
            instantSlasher: instantSlasher,
            serviceManager: serviceManager
        });

        ChallengeLib.slashSigningOperators(
            ctx, task.quorumNumbers, task.taskCreatedBlock, addressOfNonSigningOperators
        );

        taskSuccesfullyChallenged[taskId] = true;
        emit TaskChallengedSuccessfully(taskId, msg.sender);
    }

    function getTaskManagerConfig() external view returns (TaskManagerConfig memory) {
        return _taskManagerConfig;
    }

    function updateTaskManagerConfig(
        TaskManagerConfig memory taskManagerConfig
    ) external onlyOwner {
        require(taskManagerConfig.taskResponseWindowBlock > 0, InvalidTaskManagerConfig());
        require(taskManagerConfig.taskChallengeWindowBlock > 0, InvalidTaskManagerConfig());
        _taskManagerConfig = taskManagerConfig;
    }

    function _isChallengable(
        Task calldata task,
        TaskResponse calldata taskResponse,
        ResponseCertificate calldata responseCertificate,
        ChallengeData calldata challenge
    ) internal view returns (bool) {
        bytes32 taskId = taskResponse.taskId;
        return task.taskId == taskId && allTaskResponses[taskId] != bytes32(0)
            && allTaskResponses[taskId] == keccak256(abi.encode(taskResponse, responseCertificate))
            && !taskSuccesfullyChallenged[taskId]
            && uint32(block.number) <= responseCertificate.responseExpireBlock
            && challenge.taskId == taskId;
    }

    function validateAttestation(
        NewtonMessage.Attestation calldata attestation
    ) external onlyPolicyClient returns (bool) {
        TaskLib.sanityCheckAttestation(attestation);

        // Validate attestation hash
        bytes32 attestationHash = keccak256(abi.encode(attestation));
        require(attestations[attestation.taskId] == attestationHash, AttestationHashMismatch());
        require(uint32(block.number) <= attestation.expiration, AttestationExpired());

        // Prevent double spending of the same attestation by setting the attestation hash to 0
        require(attestations[attestation.taskId] != bytes32(0), AttestationAlreadySpent());
        attestations[attestation.taskId] = bytes32(0);

        emit AttestationSpent(attestation.taskId, attestation);

        return true;
    }
}
"
    },
    "lib/eigenlayer-middleware/lib/openzeppelin-contracts-upgradeable/contracts/proxy/utils/Initializable.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (proxy/utils/Initializable.sol)

pragma solidity ^0.8.2;

import "../../utils/AddressUpgradeable.sol";

/**
 * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
 * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
 * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
 * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
 *
 * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
 * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
 * case an upgrade adds a module that needs to be initialized.
 *
 * For example:
 *
 * [.hljs-theme-light.nopadding]
 * ```solidity
 * contract MyToken is ERC20Upgradeable {
 *     function initialize() initializer public {
 *         __ERC20_init("MyToken", "MTK");
 *     }
 * }
 *
 * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
 *     function initializeV2() reinitializer(2) public {
 *         __ERC20Permit_init("MyToken");
 *     }
 * }
 * ```
 *
 * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
 * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
 *
 * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
 * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
 *
 * [CAUTION]
 * ====
 * Avoid leaving a contract uninitialized.
 *
 * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
 * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
 * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
 *
 * [.hljs-theme-light.nopadding]
 * ```
 * /// @custom:oz-upgrades-unsafe-allow constructor
 * constructor() {
 *     _disableInitializers();
 * }
 * ```
 * ====
 */
abstract contract Initializable {
    /**
     * @dev Indicates that the contract has been initialized.
     * @custom:oz-retyped-from bool
     */
    uint8 private _initialized;

    /**
     * @dev Indicates that the contract is in the process of being initialized.
     */
    bool private _initializing;

    /**
     * @dev Triggered when the contract has been initialized or reinitialized.
     */
    event Initialized(uint8 version);

    /**
     * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
     * `onlyInitializing` functions can be used to initialize parent contracts.
     *
     * Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a
     * constructor.
     *
     * Emits an {Initialized} event.
     */
    modifier initializer() {
        bool isTopLevelCall = !_initializing;
        require(
            (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),
            "Initializable: contract is already initialized"
        );
        _initialized = 1;
        if (isTopLevelCall) {
            _initializing = true;
        }
        _;
        if (isTopLevelCall) {
            _initializing = false;
            emit Initialized(1);
        }
    }

    /**
     * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
     * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
     * used to initialize parent contracts.
     *
     * A reinitializer may be used after the original initialization step. This is essential to configure modules that
     * are added through upgrades and that require initialization.
     *
     * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
     * cannot be nested. If one is invoked in the context of another, execution will revert.
     *
     * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
     * a contract, executing them in the right order is up to the developer or operator.
     *
     * WARNING: setting the version to 255 will prevent any future reinitialization.
     *
     * Emits an {Initialized} event.
     */
    modifier reinitializer(uint8 version) {
        require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
        _initialized = version;
        _initializing = true;
        _;
        _initializing = false;
        emit Initialized(version);
    }

    /**
     * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
     * {initializer} and {reinitializer} modifiers, directly or indirectly.
     */
    modifier onlyInitializing() {
        require(_initializing, "Initializable: contract is not initializing");
        _;
    }

    /**
     * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
     * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
     * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
     * through proxies.
     *
     * Emits an {Initialized} event the first time it is successfully executed.
     */
    function _disableInitializers() internal virtual {
        require(!_initializing, "Initializable: contract is initializing");
        if (_initialized != type(uint8).max) {
            _initialized = type(uint8).max;
            emit Initialized(type(uint8).max);
        }
    }

    /**
     * @dev Returns the highest version that has been initialized. See {reinitializer}.
     */
    function _getInitializedVersion() internal view returns (uint8) {
        return _initialized;
    }

    /**
     * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
     */
    function _isInitializing() internal view returns (bool) {
        return _initializing;
    }
}
"
    },
    "lib/eigenlayer-middleware/lib/openzeppelin-contracts-upgradeable/contracts/access/OwnableUpgradeable.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)

pragma solidity ^0.8.0;

import "../utils/ContextUpgradeable.sol";
import "../proxy/utils/Initializable.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    function __Ownable_init() internal onlyInitializing {
        __Ownable_init_unchained();
    }

    function __Ownable_init_unchained() internal onlyInitializing {
        _transferOwnership(_msgSender());
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby disabling any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[49] private __gap;
}
"
    },
    "lib/eigenlayer-middleware/lib/eigenlayer-contracts/src/contracts/permissions/Pausable.sol": {
      "content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.27;

import "../interfaces/IPausable.sol";

/**
 * @title Adds pausability to a contract, with pausing & unpausing controlled by the `pauser` and `unpauser` of a PauserRegistry contract.
 * @author Layr Labs, Inc.
 * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service
 * @notice Contracts that inherit from this contract may define their own `pause` and `unpause` (and/or related) functions.
 * These functions should be permissioned as "onlyPauser" which defers to a `PauserRegistry` for determining access control.
 * @dev Pausability is implemented using a uint256, which allows up to 256 different single bit-flags; each bit can potentially pause different functionality.
 * Inspiration for this was taken from the NearBridge design here https://etherscan.io/address/0x3FEFc5A4B1c02f21cBc8D3613643ba0635b9a873#code.
 * For the `pause` and `unpause` functions we've implemented, if you pause, you can only flip (any number of) switches to on/1 (aka "paused"), and if you unpause,
 * you can only flip (any number of) switches to off/0 (aka "paused").
 * If you want a pauseXYZ function that just flips a single bit / "pausing flag", it will:
 * 1) 'bit-wise and' (aka `&`) a flag with the current paused state (as a uint256)
 * 2) update the paused state to this new value
 * @dev We note as well that we have chosen to identify flags by their *bit index* as opposed to their numerical value, so, e.g. defining `DEPOSITS_PAUSED = 3`
 * indicates specifically that if the *third bit* of `_paused` is flipped -- i.e. it is a '1' -- then deposits should be paused
 */
abstract contract Pausable is IPausable {
    /// Constants

    uint256 internal constant _UNPAUSE_ALL = 0;

    uint256 internal constant _PAUSE_ALL = type(uint256).max;

    /// @notice Address of the `PauserRegistry` contract that this contract defers to for determining access control (for pausing).
    IPauserRegistry public immutable pauserRegistry;

    /// Storage

    /// @dev Do not remove, deprecated storage.
    IPauserRegistry private __deprecated_pauserRegistry;

    /// @dev Returns a bitmap representing the paused status of the contract.
    uint256 private _paused;

    /// Modifiers

    /// @dev Thrown if the caller is not a valid pauser according to the pauser registry.
    modifier onlyPauser() {
        require(pauserRegistry.isPauser(msg.sender), OnlyPauser());
        _;
    }

    /// @dev Thrown if the caller is not a valid unpauser according to the pauser registry.
    modifier onlyUnpauser() {
        require(msg.sender == pauserRegistry.unpauser(), OnlyUnpauser());
        _;
    }

    /// @dev Thrown if the contract is paused, i.e. if any of the bits in `_paused` is flipped to 1.
    modifier whenNotPaused() {
        require(_paused == 0, CurrentlyPaused());
        _;
    }

    /// @dev Thrown if the `indexed`th bit of `_paused` is 1, i.e. if the `index`th pause switch is flipped.
    modifier onlyWhenNotPaused(
        uint8 index
    ) {
        require(!paused(index), CurrentlyPaused());
        _;
    }

    /// Construction

    constructor(
        IPauserRegistry _pauserRegistry
    ) {
        require(address(_pauserRegistry) != address(0), InputAddressZero());
        pauserRegistry = _pauserRegistry;
    }

    /// @inheritdoc IPausable
    function pause(
        uint256 newPausedStatus
    ) external onlyPauser {
        uint256 currentPausedStatus = _paused;
        // verify that the `newPausedStatus` does not *unflip* any bits (i.e. doesn't unpause anything, all 1 bits remain)
        require((currentPausedStatus & newPausedStatus) == currentPausedStatus, InvalidNewPausedStatus());
        _setPausedStatus(newPausedStatus);
    }

    /// @inheritdoc IPausable
    function pauseAll() external onlyPauser {
        _setPausedStatus(_PAUSE_ALL);
    }

    /// @inheritdoc IPausable
    function unpause(
        uint256 newPausedStatus
    ) external onlyUnpauser {
        uint256 currentPausedStatus = _paused;
        // verify that the `newPausedStatus` does not *flip* any bits (i.e. doesn't pause anything, all 0 bits remain)
        require(((~currentPausedStatus) & (~newPausedStatus)) == (~currentPausedStatus), InvalidNewPausedStatus());
        _paused = newPausedStatus;
        emit Unpaused(msg.sender, newPausedStatus);
    }

    /// @inheritdoc IPausable
    function paused() public view virtual returns (uint256) {
        return _paused;
    }

    /// @inheritdoc IPausable
    function paused(
        uint8 index
    ) public view virtual returns (bool) {
        uint256 mask = 1 << index;
        return ((_paused & mask) == mask);
    }

    /// @dev Internal helper for setting the paused status, and emitting the corresponding event.
    function _setPausedStatus(
        uint256 pausedStatus
    ) internal {
        _paused = pausedStatus;
        emit Paused(msg.sender, pausedStatus);
    }

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[48] private __gap;
}
"
    },
    "lib/eigenlayer-middleware/src/interfaces/ISlashingRegistryCoordinator.sol": {
      "content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.27;

import {IBLSApkRegistry} from "./IBLSApkRegistry.sol";
import {IStakeRegistry} from "./IStakeRegistry.sol";
import {IIndexRegistry} from "./IIndexRegistry.sol";
import {BN254} from "../libraries/BN254.sol";
import {IAllocationManager} from
    "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol";
import {IBLSApkRegistry} from "./IBLSApkRegistry.sol";
import {IStakeRegistry, IStakeRegistryTypes} from "./IStakeRegistry.sol";
import {IIndexRegistry} from "./IIndexRegistry.sol";
import {ISocketRegistry} from "./ISocketRegistry.sol";
import {BN254} from "../libraries/BN254.sol";
import {IAVSRegistrar} from "eigenlayer-contracts/src/contracts/interfaces/IAVSRegistrar.sol";

interface ISlashingRegistryCoordinatorErrors {
    /// @notice Thrown when array lengths in input parameters don't match.
    error InputLengthMismatch();
    /// @notice Thrown when an invalid registration type is provided.
    error InvalidRegistrationType();
    /// @notice Thrown when non-allocation manager calls restricted function.
    error OnlyAllocationManager();
    /// @notice Thrown when non-ejector calls restricted function.
    error OnlyEjector();
    /// @notice Thrown when operating on a non-existent quorum.
    error QuorumDoesNotExist();
    /// @notice Thrown when registering/deregistering with empty bitmap.
    error BitmapEmpty();
    /// @notice Thrown when registering for already registered quorums.
    error AlreadyRegisteredForQuorums();
    /// @notice Thrown when registering before ejection cooldown expires.
    error CannotReregisterYet();
    /// @notice Thrown when unregistered operator attempts restricted operation.
    error NotRegistered();
    /// @notice Thrown when operator attempts self-churn.
    error CannotChurnSelf();
    /// @notice Thrown when operator count doesn't match quorum requirements.
    error QuorumOperatorCountMismatch();
    /// @notice Thrown when operator has insufficient stake for churn.
    error InsufficientStakeForChurn();
    /// @notice Thrown when attempting to kick operator above stake threshold.
    error CannotKickOperatorAboveThreshold();
    /// @notice Thrown when updating to zero bitmap.
    error BitmapCannotBeZero();
    /// @notice Thrown when deregistering from unregistered quorum.
    error NotRegisteredForQuorum();
    /// @notice Thrown when churn approver salt is already used.
    error ChurnApproverSaltUsed();
    /// @notice Thrown when operators or quorums list is not sorted ascending.
    error NotSorted();
    /// @notice Thrown when maximum quorum count is reached.
    error MaxQuorumsReached();
    /// @notice Thrown when the provided AVS address does not match the expected one.
    error InvalidAVS();
    /// @notice Thrown when attempting to kick an operator that is not registered.
    error OperatorNotRegistered();
    /// @notice Thrown when lookAheadPeriod is greater than or equal to DEALLOCATION_DELAY.
    error LookAheadPeriodTooLong();
    /// @notice Thrown when the number of operators in a quorum would exceed the maximum allowed.
    error MaxOperatorCountReached();
}

interface ISlashingRegistryCoordinatorTypes {
    /// @notice Core data structure for tracking operator information.
    /// @dev Links an operator's unique identifier with their current registration status.
    /// @param operatorId Unique identifier for the operator, typically derived from their BLS public key.
    /// @param status Current registration state of the operator in the system.
    struct OperatorInfo {
        bytes32 operatorId;
        OperatorStatus status;
    }

    /// @notice Records historical changes to an operator's quorum registrations.
    /// @dev Used for querying an operator's quorum memberships at specific block numbers.
    /// @param updateBlockNumber Block number when this update occurred (inclusive).
    /// @param nextUpdateBlockNumber Block number when the next update occurred (exclusive), or 0 if this is the latest update.
    /// @param quorumBitmap Bitmap where each bit represents registration in a specific quorum (1 = registered, 0 = not registered).
    struct QuorumBitmapUpdate {
        uint32 updateBlockNumber;
        uint32 nextUpdateBlockNumber;
        uint192 quorumBitmap;
    }

    /// @notice Configuration parameters for operator management within a quorum.
    /// @dev All BIPs (Basis Points) values are in relation to BIPS_DENOMINATOR (10000).
    /// @param maxOperatorCount Maximum number of operators allowed in the quorum.
    /// @param kickBIPsOfOperatorStake Required stake ratio (in BIPs) between new and existing operator for churn.
    ///        Example: 10500 means new operator needs 105% of existing operator's stake.
    /// @param kickBIPsOfTotalStake Minimum stake ratio (in BIPs) of total quorum stake an operator must maintain.
    ///        Example: 100 means operator needs 1% of total quorum stake to avoid being churned.
    struct OperatorSetParam {
        uint32 maxOperatorCount;
        uint16 kickBIPsOfOperatorStake;
        uint16 kickBIPsOfTotalStake;
    }

    /// @notice Parameters for removing an operator during churn.
    /// @dev Used in registerOperatorWithChurn to specify which operator to replace.
    /// @param quorumNumber The quorum from which to remove the operator.
    /// @param operator Address of the operator to be removed.
    struct OperatorKickParam {
        uint8 quorumNumber;
        address operator;
    }

    /// @notice Represents the registration state of an operator.
    /// @dev Used to track an operator's lifecycle in the system.
    /// @custom:enum NEVER_REGISTERED The operator has never registered with the system.
    /// @custom:enum REGISTERED The operator is currently registered and active.
    /// @custom:enum DEREGISTERED The operator was previously registered but has since deregistered.
    enum OperatorStatus {
        NEVER_REGISTERED,
        REGISTERED,
        DEREGISTERED
    }

    /**
     * @notice Enum representing the type of operator registration.
     * @custom:enum NORMAL Represents a normal operator registration.
     * @custom:enum CHURN Represents an operator registration during a churn event.
     */
    enum RegistrationType {
        NORMAL,
        CHURN
    }

    /**
     * @notice Data structure for storing the results of a registerOperator call.
     * @dev Contains arrays storing per-quorum information about operator counts and stakes.
     * @param numOperatorsPerQuorum For each quorum the operator registered for, stores the number of operators registered.
     * @param operatorStakes For each quorum the operator registered for, stores the stake of the operator in the quorum.
     * @param totalStakes For each quorum the operator registered for, stores the total stake of the quorum.
     */
    struct RegisterResults {
        uint32[] numOperatorsPerQuorum;
        uint96[] operatorStakes;
        uint96[] totalStakes;
    }
}

interface ISlashingRegistryCoordinatorEvents is ISlashingRegistryCoordinatorTypes {
    /**
     * @notice Emitted when an operator registers for service in one or more quorums.
     * @dev Emitted in _registerOperator() and _registerOperatorToOperatorSet().
     * @param operator The address of the registered operator.
     * @param operatorId The unique identifier of the operator (BLS public key hash).
     */
    event OperatorRegistered(address indexed operator, bytes32 indexed operatorId);

    /**
     * @notice Emitted when an operator deregisters from service in one or more quorums.
     * @dev Emitted in _deregisterOperator().
     * @param operator The address of the deregistered operator.
     * @param operatorId The unique identifier of the operator (BLS public key hash).
     */
    event OperatorDeregistered(address indexed operator, bytes32 indexed operatorId);

    /**
     * @notice Emitted when a new quorum is created.
     * @param quorumNumber The identifier of the quorum being created.
     * @param operatorSetParams The operator set parameters for the quorum.
     * @param minimumStake The minimum stake required for operators in this quorum.
     * @param strategyParams The strategy parameters for stake calculation.
     * @param stakeType The type of stake being tracked (TOTAL_DELEGATED or TOTAL_SLASHABLE).
     * @param lookAheadPeriod The number of blocks to look ahead when calculating slashable stake (only used for TOTAL_SLASHABLE).
     */
    event QuorumCreated(
        uint8 indexed quorumNumber,
        OperatorSetParam operatorSetParams,
        uint96 minimumStake,
        IStakeRegistryTypes.StrategyParams[] strategyParams,
        IStakeRegistryTypes.StakeType stakeType,
        uint32 lookAheadPeriod
    );

    /**
     * @notice Emitted when a quorum's operator set parameters are updated.
     * @dev Emitted in _setOperatorSetParams().
     * @param quorumNumber The identifier of the quorum being updated.
     * @param operatorSetParams The new operator set parameters for the quorum.
     */
    event OperatorSetParamsUpdated(uint8 indexed quorumNumber, OperatorSetParam operatorSetParams);

    /**
     * @notice Emitted when the churn approver address is updated.
     * @dev Emitted in _setChurnApprover().
     * @param prevChurnApprover The previous churn approver address.
     * @param newChurnApprover The new churn approver address.
     */
    event ChurnApproverUpdated(address prevChurnApprover, address newChurnApprover);

    /**
     * @notice Emitted when the AVS address is updated.
     * @param prevAVS The previous AVS address.
     * @param newAVS The new AVS address.
     */
    event AVSUpdated(address prevAVS, address newAVS);

    /**
     * @notice Emitted when the ejector address is updated.
     * @dev Emitted in _setEjector().
     * @param prevEjector The previous ejector address.
     * @param newEjector The new ejector address.
     */
    event EjectorUpdated(address prevEjector, address newEjector);

    /**
     * @notice Emitted when all operators in a quorum are updated simultaneously.
     * @dev Emitted in updateOperatorsForQuorum().
     * @param quorumNumber The identifier of the quorum being updated.
     * @param blocknumber The block number at which the quorum update occurred.
     */
    event QuorumBlockNumberUpdated(uint8 indexed quorumNumber, uint256 blocknumber);

    /**
     * @notice Emitted when an operator's socket is updated.
     * @dev Emitted in updateSocket().
     * @param operatorId The unique identifier of the operator (BLS public key hash).
     * @param socket The new socket address for the operator (typically an IP address).
     */
    event OperatorSocketUpdate(bytes32 indexed operatorId, string socket);

    /**
     * @notice Emitted when the ejection cooldown period is updated.
     * @dev Emitted in setEjectionCooldown().
     * @param prevEjectionCooldown The previous cooldown duration in seconds.
     * @param newEjectionCooldown The new cooldown duration in seconds.
     */
    event EjectionCooldownUpdated(uint256 prevEjectionCooldown, uint256 newEjectionCooldown);
}

interface ISlashingRegistryCoordinator is
    IAVSRegistrar,
    ISlashingRegistryCoordinatorErrors,
    ISlashingRegistryCoordinatorEvents
{
    /// IMMUTABLES & CONSTANTS

    /**
     * @notice EIP-712 typehash for operator churn approval signatures.
     * @return The typehash constant.
     */
    function OPERATOR_CHURN_APPROVAL_TYPEHASH() external view returns (bytes32);

    /**
     * @notice EIP-712 typehash for pubkey registration signatures.
     * @return The typehash constant.
     */
    function PUBKEY_REGISTRATION_TYPEHASH() external view returns (bytes32);

    /**
     * @notice Reference to the BLSApkRegistry contract.
     * @return The BLSApkRegistry contract interface.
     */
    function blsApkRegistry() external view returns (IBLSApkRegistry);

    /**
     * @notice Reference to the StakeRegistry contract.
     * @return The StakeRegistry contract interface.
     */
    function stakeRegistry() external view returns (IStakeRegistry);

    /**
     * @notice Reference to the IndexRegistry contract.
     * @return The IndexRegistry contract interface.
     */
    function indexRegistry() external view returns (IIndexRegistry);

    /**
     * @notice Reference to the AllocationManager contract.
     * @return The AllocationManager contract interface.
     * @dev This is only relevant for Slashing AVSs
     */
    function allocationManager() external view returns (IAllocationManager);

    /**
     * @notice Reference to the SocketRegistry contract.
     * @return The SocketRegistry contract interface.
     */
    function socketRegistry() external view returns (ISocketRegistry);

    /// STORAGE

    /**
     * @notice The total number of quorums that have been created.
     * @return The count of quorums.
     */
    function quorumCount() external view returns (uint8);

    /**
     * @notice Checks if a churn approver salt has been used.
     * @param salt The salt to check.
     * @return True if the salt has been used, false otherwise.
     */
    function isChurnApproverSaltUsed(
        bytes32 salt
    ) external view returns (bool);

    /**
     * @notice Gets the last block number when all operators in a quorum were updated.
     * @param quorumNumber The quorum identifier.
     * @return The block number of the last update.
     */
    function quorumUpdateBlockNumber(
        uint8 quorumNumber
    ) external view returns (uint256);

    /**
     * @notice The address authorized to approve operator churn operations.
     * @return The churn approver address.
     */
    function churnApprover() external view returns (address);

    /**
     * @notice The address authorized to forcibly eject operators.
     * @return The ejector address.
     */
    function ejector() external view returns (address);

    /**
     * @notice Gets the timestamp of an operator's last ejection.
     * @param operator The operator address.
     * @return The timestamp of the last ejection.
     */
    function lastEjectionTimestamp(
        address operator
    ) external view returns (uint256);

    /**
     * @notice The cooldown period after ejection before an operator can re-register.
     * @return The cooldown duration in seconds.
     */
    function ejectionCooldown() external view returns (uint256);

    /// ACTIONS

    /**
     * @notice Updates stake weights for specified operators. If any operator is found to be below
     * the minimum stake for their registered quorums, they are deregistered from those quorums.
     * @param operators The operators whose stakes should be updated.
     * @dev Stakes are queried from the Eigenlayer core DelegationManager contract.
     * @dev WILL BE DEPRECATED IN FAVOR OF updateOperatorsForQuorum
     */
    function updateOperators(
        address[] memory operators
    ) external;

    /**
     * @notice For each quorum in `quorumNumbers`, updates the StakeRegistry's view of ALL its registered operators' stakes.
     * Each quorum's `quorumUpdateBlockNumber` is also updated, which tracks the most recent block number when ALL registered
     * operators were updated.
     * @dev stakes are queried from the Eigenlayer core DelegationManager contract
     * @param operatorsPerQuorum for each quorum in `quorumNumbers`, this has a corresponding list of operators to update.
     * @dev Each list of operator addresses MUST be sorted in ascending order
     * @dev Each list of operator addresses MUST represent the entire list of registered operators for the corresponding quorum
     * @param quorumNumbers is an ordered byte array containing the quorum numbers being updated
     * @dev invariant: Each list of `operatorsPerQuorum` MUST be a sorted version of `IndexRegistry.getOperatorListAtBlockNumber`
     * for the corresponding quorum.
     * @dev note on race condition: if an operator registers/deregisters for any quorum in `quorumNumbers` after a txn to
     * this method is broadcast (but before it is executed), the method will fail
     */
    function updateOperatorsForQuorum(
        address[][] memory operatorsPerQuorum,
        bytes calldata quorumNumbers
    ) external;

    /**
     * @notice Updates the socket of the msg.sender given they are a registered operator.
     * @param socket The new socket address for the operator (typically an IP address).
     * @dev Will revert if msg.sender is not a registered operator.
     */
    function updateSocket(
        string memory socket
    ) external;

    /**
     * @notice Forcibly removes an operator from specified quorums and sets their ejection timestamp.
     * @param operator The operator address to eject.
     * @param quorumNumbers The quorum numbers to eject the operator from.
     * @dev Can only be called by the ejector address.
     * @dev The operator cannot re-register until ejectionCooldown period has passed.
     */
    function ejectOperator(address operator, bytes memory quorumNumbers) external;

    /**
     * @notice Creates a new quorum that tracks total delegated stake for operators.
     * @param operatorSetParams Configures the quorum's max operator count and churn parameters.
     * @param minimumStake Sets the minimum stake required for an operator to register or remain registered.
     * @param strategyParams A list of strategies and multipliers used by the StakeRegistry to calculate
     * an operator's stake weight for the quorum.
     * @dev For m2 AVS this function has the same behavior as createQuorum before.
     * @dev For migrated AVS that enable operator sets this will create a quorum that measures total delegated stake for operator set.
     */
    function createTotalDelegatedStakeQuorum(
        OperatorSetParam memory operatorSetParams,
        uint96 minimumStake,
        IStakeRegistryTypes.StrategyParams[] memory strategyParams
    ) external;

    /**
     * @notice Creates a new quorum that tracks slashable stake for operators.
     * @param operatorSetParams Configures the quorum's max operator count and churn parameters.
     * @param minimumStake Sets the minimum stake required for an operator to register or remain registered.
     * @param strategyParams A list of strategies and multipliers used by the StakeRegistry to calculate
     * an operator's stake weight for the quorum.
     * @param lookAheadPeriod The number of blocks to look ahead when calculating slashable stake.
     * @dev Can only be called when operator sets are enabled.
     */
    function createSlashableStakeQuorum(
        OperatorSetParam memory operatorSetParams,
        uint96 minimumStake,
        IStakeRegistryTypes.StrategyParams[] memory strategyParams,
        uint32 lookAheadPeriod
    ) external;

    /**
     * @notice Updates the configuration parameters for an existing operator set quorum.
     * @param quorumNumber The identifier of the quorum to update.
     * @param operatorSetParams The new operator set parameters to apply.
     * @dev Can only be called by the contract owner.
     */
    function setOperatorSetParams(
        uint8 quorumNumber,
        OperatorSetParam memory operatorSetParams
    ) external;

    /**
     * @notice Updates the address authorized to approve operator churn operations.
     * @param _churnApprover The new churn approver address.
     * @dev Can only be called by the contract owner.
     * @dev The churn approver is responsible for signing off on operator replacements in full quorums.
     */
    function setChurnApprover(
        address _churnApprover
    ) external;

    /**
     * @notice Updates the address authorized to forcibly eject operators.
     * @param _ejector The new ejector address.
     * @dev Can only be called by the contract owner.
     * @dev The ejector can force-remove operators from quorums regardless of their stake.
     */
    function setEjector(
        address _ejector
    ) external;

    /**
     * @notice Updates the duration operators must wait after ejection before re-registering.
     * @param _ejectionCooldown The new cooldown duration in seconds.
     * @dev Can only be called by the contract owner.
     */
    function setEjectionCooldown(
        uint256 _ejectionCooldown
    ) external;

    /**
     * @notice Updates the avs address for this AVS (used for UAM integration in EigenLayer)
     * @param _avs The new avs address
     * @dev Can only be called by the contract owner
     * @dev NOTE: Updating this value will break existing OperatorSets and UAM integration. This value should only be set once.
     */
    function setAVS(
        address _avs
    ) external;

    /// VIEW

    /**
     * @notice Returns the hash of the message that operators must sign with their BLS key to register
     * @param operator The operator's Ethereum address
     */
    function calculatePubkeyRegistrationMessageHash(
        address operator
    ) external view returns (bytes32);

    /**
     * @notice Returns the operator set parameters for a given quorum.
     * @param quorumNumber The identifier of the quorum to query.
     * @return The OperatorSetParam struct containing max operator count and churn thresholds.
     */
    function getOperatorSetParams(
        uint8 quorumNumber
    ) external view returns (OperatorSetParam memory);

    /**
     * @notice Returns the complete operator information for a given address.
     * @param operator The operator address to query.
     * @return An OperatorInfo struct containing the operator's ID and registration status.
     */
    function getOperator(
        address operator
    ) external view returns (OperatorInfo memory);

    /**
     * @notice Returns the unique identifier for a given operator address.
     * @param operator The operator address to query.
     * @return The operator's ID (derived from their BLS public key hash).
     */
    function getOperatorId(
        address operator
    ) external view returns (bytes32);

    /**
     * @notice Returns the operator address associated with a given operator ID.
     * @param operatorId The unique identifier to look up.
     * @return The operator's address.
     * @dev Returns address(0) if the ID is not registered.
     */
    function getOperatorFromId(
        bytes32 operatorId
    ) external view returns (address);

    /**
     * @notice Returns the current registration status for a given operator.
     * @param operator The operator address to query.
     * @return The operator's status (NEVER_REGISTERED, REGISTERED, or DEREGISTERED).
     */
    function getOperatorStatus(
        address operator
    ) external view returns (OperatorStatus);

    /**
     * @notice Returns the indices needed to look up quorum bitmaps for operators at a specific block.
     * @param blockNumber The historical block number to query.
     * @param operatorIds Array of operator IDs to get indices for.
     * @return Array of indices corresponding to each operator ID.
     * @dev Reverts if any operator had not yet registered at the specified block.
     * @dev This function is designed to find proper inputs for getQuorumBitmapAtBlockNumberByIndex.
     */
    function getQuorumBitmapIndicesAtBlockNumber(
        uint32 blockNumber,
        bytes32[] memory operatorIds
    ) external view returns (uint32[] memory);

    /**
     * @notice Returns the quorum bitmap for an operator at a specific historical block.
     * @param operatorId The operator's unique identifier.
     * @param blockNumber The historical block number to query.
     * @param index The index in the operator's bitmap history (from getQuorumBitmapIndicesAtBlockNumber).
     * @return The quorum bitmap showing which quorums the operator was registered for.
     * @dev Reverts if the index is incorrect for the specified block number.
     */
    function getQuorumBitmapAtBlockNumberByIndex(
        bytes32 operatorId,
        uint32 blockNumber,
        uint256 index
    ) external view returns (uint192);

    /**
     * @notice Returns a specific update from an operator's quorum bitmap history.
     * @param operatorId The operator's unique identifier.
     * @param index The index in the bitmap history to query.
     * @return The QuorumBitmapUpdate struct at that index.
     */
    function getQuorumBitmapUpdateByIndex(
        bytes32 operatorId,
        uint256 index
    ) external view returns (QuorumBitmapUpdate memory);

    /**
     * @notice Returns the current quorum bitmap for an operator.
     * @param operatorId The operator's unique identifier.
     * @return A bitmap where each bit represents registration in a specific quorum.
     * @dev Returns 0 if the operator is not registered for any quorums.
     */
    function getCurrentQuorumBitmap(
        bytes32 operatorId
    ) external view returns (uint192);

    /**
     * @notice Returns the number of updates in an operator's bitmap history.
     * @param operatorId The operator's unique identifier.
     * @return The length of the bitmap history array.
     */
    function getQuorumBitmapHistoryLength(
        bytes32 operatorId
    ) external view returns (uint256);

    /**
     * @notice Calculates the digest hash that must be signed by the churn approver.
     * @param registeringOperator The address of the operator attempting to register.
     * @param registeringOperatorId The unique ID of the registering operator.
     * @param operatorKickParams Parameters specifying which operators to replace in full quorums.
     * @param salt Random value to ensure signature uniqueness.
     * @param expiry Timestamp after which the signature becomes invalid.
     * @return The EIP-712 typed data hash to be signed.
     */
    function calculateOperatorChurnApprovalDigestHash(
        address registeringOperator,
        bytes32 registeringOperatorId,
        OperatorKickParam[] memory operatorKickParams,
        bytes32 salt,
        uint256 expiry
    ) external view returns (bytes32);

    /**
     * @notice Returns the message hash that an operator must sign to register their BLS public key.
     * @param operator The address of the operator registering their key.
     * @return A point on the G1 curve representing the message hash.
     */
    function pubkeyRegistrationMessageHash(
        address operator
    ) external view returns (BN254.G1Point memory);

    /**
     * @notice Returns the avs address for this AVS (used for UAM integration in EigenLayer)
     * @dev NOTE: Updating this value will break existing OperatorSets and UAM integration. This value should only be set once.
     * @return The avs address
     */
    function avs() external view returns (address);
}
"
    },
    "lib/eigenlayer-middleware/src/OperatorStateRetriever.sol": {
      "content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.27;

import {ISlashingRegistryCoordinator} from "./interfaces/ISlashingRegistryCoordinator.sol";
import {IBLSApkRegistry} from "./interfaces/IBLSApkRegistry.sol";
import {IStakeRegistry} from "./interfaces/IStakeRegistry.sol";
import {IIndexRegistry} from "./interfaces/IIndexRegistry.sol";

import {BitmapUtils} from "./libraries/BitmapUtils.sol";

/**
 * @title OperatorStateRetriever with view functions that allow to retrieve the state of an AVSs registry system.
 * @author Layr Labs Inc.
 */
contract OperatorStateRetriever {
    struct Operator {
        address operator;
        bytes32 operatorId;
        uint96 stake;
    }

    struct CheckSignaturesIndices {
        uint32[] nonSignerQuorumBitmapIndices;
        uint32[] quorumApkIndices;
        uint32[] totalStakeIndices;
        uint32[][] nonSignerStakeIndices; // nonSignerStakeIndices[quorumNumberIndex][nonSignerIndex]
    }

    error OperatorNotRegistered();

    /**
     * @notice This function is intended to to be called by AVS operators every time a new task is created (i.e.)
     * the AVS coordinator makes a request to AVS operators. Since all of the crucial information is kept onchain,
     * operators don't need to run indexers to fetch the data.
     * @param registryCoordinator is the registry coordinator to fetch the AVS registry information from
     * @param operatorId the id of the operator to fetch the quorums lists
     * @param blockNumber is the block number to get the operator state for
     * @return 1) the quorumBitmap of the operator at the given blockNumber
     *         2) 2d array of Operator structs. For each quorum the provided operator
     *            was a part of at `blockNumber`, an ordered list of operators.
     */
    function getOperatorState(
        ISlashingRegistryCoordinator registryCoordinator,
        bytes32 operatorId,
        uint32 blockNumber
    ) external view returns (uint256, Operator[][] memory) {
        bytes32[] memory operatorIds = new bytes32[](1);
        operatorIds[0] = operatorId;
        uint256 index =
            registryCoordinator.getQuorumBitmapIndicesAtBlockNumber(blockNumber, operatorIds)[0];

        uint256 quorumBitmap =
            registryCoordinator.getQuorumBitmapAtBlockNumberByIndex(operatorId, blockNumber, index);

        bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap);

        return (quorumBitmap, getOperatorState(registryCoordinator, quorumNumbers, blockNumber));
    }

    /**
     * @notice returns the ordered list of operators (id and stake) for each quorum. The AVS coordinator
     * may call this function directly to get the operator state for a given block number
     * @param registryCoordinator is the registry coordinator to fetch the AVS registry information from
     * @param quorumNumbers are the ids of the quorums to get the operator state for
     * @param blockNumber is the block number to get the operator state for
     * @return 2d array of Operators. For each quorum, an ordered list of Operators
     */
    function getOperatorState(
        ISlashingRegistryCoordinator registryCoordinator,
        bytes memory quorumNumbers,
        uint32 blockNumber
    ) public view returns (Operator[][] memory) {
        IStakeRegistry stakeRegistry = registryCoordinator.stakeRegistry();
        IIndexRegistry indexRegistry = registryCoordinator.indexRegistry();
        IBLSApkRegistry blsApkRegistry = registryCoordinator.blsApkRegistry();

        Operator[][] memory operators = new Operator[][](quorumNumbers.length);
        for (uint256 i = 0; i < quorumNumbers.length; i++) {
            uint8 quorumNumber = uint8(quorumNumbers[i]);
            bytes32[] memory operatorIds =
                indexRegistry.getOperatorListAtBlockNumber(quorumNumber, blockNumber);
            operators[i] = new Operator[](operatorIds.length);
            for (uint256 j = 0; j < operatorIds.length; j++) {
                operators[i][j] = Operator({
                    operator: blsApkRegistry.getOperatorFromPubkeyHash(operatorIds[j]),
                    operatorId: bytes32(operatorIds[j]),
                    stake: stakeRegistry.getStakeAtBlockNumber(
                        bytes32(operatorIds[j]), quorumNumber, blockNumber
                    )
                });
            }
        }

        return operators;
    }

    /**
     * @notice this is called by the AVS operator to get the relevant indices for the checkSignatures function
     * if they are not running an indexer
     * @param registryCoordinator is the registry coordinator to fetch the AVS registry information from
     * @param referenceBlockNumber is the block number to get the indices for
     * @param quorumNumbers are the ids of the quorums to get the operator state for
     * @param nonSignerOperatorIds are the ids of the nonsigning operators
     * @return 1) the indices of the quorumBitmaps for each of the operators in the @param nonSignerOperatorIds array at the given blocknumber
     *         2) the indices of the total stakes entries for the given quorums at the given blocknumber
     *         3) the indices of the stakes of each of the nonsigners in each of the quorums they were a
     *            part of (for each nonsigner, an array of length the number of quorums they were a part of
     *            that are also part of the provided quorumNumbers) at the given blocknumber
     *         4) the indices of the quorum apks for each of the provided quorums at the given blocknumber
     */
    function getCheckSignaturesIndices(
        ISlashingRegistryCoordinator registryCoordinator,
        uint32 referenceBlockNumber,
        bytes calldata quorumNumbers,
        bytes32[] calldata nonSignerOperatorIds
    ) external view returns (CheckSignaturesIndices memory) {
        IStakeRegistry stakeRegistry = registryCoordinator.stakeRegistry();
        CheckSignaturesIndices memory checkSignaturesIndices;

        // get the indices of the quorumBitmap updates for each of the operators in the nonSignerOperatorIds array
        checkSignaturesIndices.nonSignerQuorumBitmapIndices = registryCoordinator
            .getQuorumBitmapIndicesAtBlockNumber(referenceBlockNumber, nonSignerOperatorIds);

        // get the indices of the totalStake updates for each of the quorums in the quorumNumbers array
        checkSignaturesIndices.totalStakeIndices =
            stakeRegistry.getTotalStakeIndicesAtBlockNumber(referenceBlockNumber, quorumNumbers);

        checkSignaturesIndices.nonSignerStakeIndices = new uint32[][](quorumNumbers.length);
        for (
            uint8 quorumNumberIndex = 0;
            quorumNumberIndex < quorumNumbers.length;
            quorumNumberIndex++
        ) {
            uint256 numNonSignersForQuorum = 0;
            // this array's length will be at most the number of nonSignerOperatorIds, this will be trimmed after it is filled
            checkSignaturesIndices.nonSignerStakeIndices[quorumNumberIndex] =
                new uint32[](nonSignerOperatorIds.length);

            for (uint256 i = 0; i < nonSignerOperatorIds.length; i++) {
                // get the quorumBitmap for the operator at the given blocknumber and index
                uint192 nonSignerQuorumBitmap = registryCoordinator
                    .getQuorumBitmapAtBlockNumberByIndex(
                    nonSignerOperatorIds[i],
                    referenceBlockNumber,
                    checkSignaturesIndices.nonSignerQuorumBitmapIndices[i]
                );

                require(nonSignerQuorumBitmap != 0, OperatorNotRegistered());

                // if the operator was a part of the quorum and the quorum is a part of the provided quorumNumbers
                if ((nonSignerQuorumBitmap >> uint8(quorumNumbers[quorumNumberIndex])) & 1 == 1) {
                    // get the index of the stake update for the operator at the given blocknumber and quorum number
                    checkSignaturesIndices.nonSignerStakeIndices[quorumNumberIndex][numNonSignersForQuorum]
                    = stakeRegistry.getStakeUpdateIndexAtBlockNumber(
                        nonSignerOperatorIds[i],
                        uint8(quorumNumbers[quorumNumberIndex]),
                        referenceBlockNumber
                    );
                    numNonSignersForQuorum++;
                }
            }

            // resize the array to the number of nonSigners for this quorum
            uint32[] memory nonSignerStakeIndicesForQuorum = new uint32[](numNonSignersForQuorum);
            for (uint256 i = 0; i < numNonSignersForQuorum; i++) {
                nonSignerStakeIndicesForQuorum[i] =
                    checkSignaturesIndices.nonSignerStakeIndices[quorumNumberIndex][i];
            }
            checkSignaturesIndices.nonSignerStakeIndices[quorumNumberIndex] =
                nonSignerStakeIndicesForQuorum;
        }

        IBLSApkRegistry blsApkRegistry = registryCoordinator.blsApkRegistry();
        // get the indices of the quorum apks for each of the provided quorums at the given blocknumber
        checkSignaturesIndices.quorumApkIndices =
            blsApkRegistry.getApkIndicesAtBlockNumber(quorumNumbers, referenceBlockNumber);

        return checkSignaturesIndices;
    }

    /**
     * @notice this function returns the quorumBitmaps for each of the operators in the operatorIds array at the given blocknumber
     * @param registryCoordinator is the AVS registry coordinator to fetch the operator information from
     * @param operatorIds are the ids of the operators to get the quorumBitmaps for
     * @param blockNumber is the block number to get the quorumBitmaps for
     */
    function getQuorumBitmapsAtBlockNumber(
        ISlashingRegistryCoordinator registryCoordinator,
        bytes32[] memory operatorIds,
        uint32 blockNumber
    ) external view returns (uint256[] memory) {
        uint32[] memory quorumBitmapIndices =
            registryCoordinator.getQuorumBitmapIndicesAtBlockNumber(blockNumber, operatorIds);
        uint256[] memory quorumBitmaps = new uint256[](operatorIds.length);
        for (uint256 i = 0; i < operatorIds.length; i++) {
            quorumBitmaps[i] = registryCoordinator.getQuorumBitmapAtBlockNumberByIndex(
                operatorIds[i], blockNumber, quorumBitmapIndices[i]
            );
        }
        return quorumBitmaps;
    }

    /**
     * @notice This function retu

Tags:
ERC20, ERC165, Multisig, Pausable, Voting, Upgradeable, Multi-Signature, Factory|addr:0xcdfe0f615eb84dfbb559ebce9cabafd892d525c6|verified:true|block:23429552|tx:0x03c89787bce8b7f2424f92530307f969154a3bf8e018678367b5c220fa9689bc|first_check:1758730221

Submitted on: 2025-09-24 18:10:25

Comments

Log in to comment.

No comments yet.