From 78245215af76260caa0c023ea5d258ff10ca7f3b Mon Sep 17 00:00:00 2001 From: "clandestine.eth" <96172957+0xClandestine@users.noreply.github.com> Date: Tue, 5 Aug 2025 11:32:36 -0400 Subject: [PATCH 1/2] feat: add `hasActiveGenerationReservation` --- .../interfaces/ICrossChainRegistry.sol | 12 ++++++++++++ src/contracts/multichain/CrossChainRegistry.sol | 17 ++++++++++++----- src/test/unit/CrossChainRegistryUnit.t.sol | 17 +++++++++++++++++ 3 files changed, 41 insertions(+), 5 deletions(-) diff --git a/src/contracts/interfaces/ICrossChainRegistry.sol b/src/contracts/interfaces/ICrossChainRegistry.sol index 9dab305392..540a4d685d 100644 --- a/src/contracts/interfaces/ICrossChainRegistry.sol +++ b/src/contracts/interfaces/ICrossChainRegistry.sol @@ -269,8 +269,18 @@ interface ICrossChainRegistry is ICrossChainRegistryErrors, ICrossChainRegistryE uint256 endIndex ) external view returns (OperatorSet[] memory); + /** + * @notice Checks if a given operatorSet has an active generation reservation + * @param operatorSet the operatorSet to check + * @return True if the operatorSet has an active generation reservation, false otherwise + */ + function hasActiveGenerationReservation( + OperatorSet memory operatorSet + ) external view returns (bool); + /** * @notice Gets the operatorTableCalculator for a given operatorSet + * @dev You may want to query `hasActiveGenerationReservation` before calling this method * @param operatorSet the operatorSet to get the operatorTableCalculator for * @return The operatorTableCalculator for the given operatorSet */ @@ -280,6 +290,7 @@ interface ICrossChainRegistry is ICrossChainRegistryErrors, ICrossChainRegistryE /** * @notice Gets the operatorSetConfig for a given operatorSet + * @dev You may want to query `hasActiveGenerationReservation` before calling this method. * @param operatorSet the operatorSet to get the operatorSetConfig for * @return The operatorSetConfig for the given operatorSet */ @@ -289,6 +300,7 @@ interface ICrossChainRegistry is ICrossChainRegistryErrors, ICrossChainRegistryE /** * @notice Calculates the operatorTableBytes for a given operatorSet + * @dev You may want to query `hasActiveGenerationReservation` before calling this method. * @param operatorSet the operatorSet to calculate the operator table for * @return the encoded operatorTableBytes containing: * - operatorSet details diff --git a/src/contracts/multichain/CrossChainRegistry.sol b/src/contracts/multichain/CrossChainRegistry.sol index ca37731fc3..fb4e62c91b 100644 --- a/src/contracts/multichain/CrossChainRegistry.sol +++ b/src/contracts/multichain/CrossChainRegistry.sol @@ -45,10 +45,10 @@ contract CrossChainRegistry is _; } - modifier hasActiveGenerationReservation( + modifier checkHasActiveGenerationReservation( OperatorSet calldata operatorSet ) { - require(_activeGenerationReservations.contains(operatorSet.key()), GenerationReservationDoesNotExist()); + require(hasActiveGenerationReservation(operatorSet), GenerationReservationDoesNotExist()); _; } @@ -135,7 +135,7 @@ contract CrossChainRegistry is onlyWhenNotPaused(PAUSED_GENERATION_RESERVATIONS) checkCanCall(operatorSet.avs) isValidOperatorSet(operatorSet) - hasActiveGenerationReservation(operatorSet) + checkHasActiveGenerationReservation(operatorSet) { bytes32 operatorSetKey = operatorSet.key(); @@ -162,7 +162,7 @@ contract CrossChainRegistry is onlyWhenNotPaused(PAUSED_OPERATOR_TABLE_CALCULATOR) checkCanCall(operatorSet.avs) isValidOperatorSet(operatorSet) - hasActiveGenerationReservation(operatorSet) + checkHasActiveGenerationReservation(operatorSet) { // Set the operator table calculator _setOperatorTableCalculator(operatorSet, operatorTableCalculator); @@ -177,7 +177,7 @@ contract CrossChainRegistry is onlyWhenNotPaused(PAUSED_OPERATOR_SET_CONFIG) checkCanCall(operatorSet.avs) isValidOperatorSet(operatorSet) - hasActiveGenerationReservation(operatorSet) + checkHasActiveGenerationReservation(operatorSet) { // Set the operator set config _setOperatorSetConfig(operatorSet, config); @@ -291,6 +291,13 @@ contract CrossChainRegistry is return operatorSets; } + /// @inheritdoc ICrossChainRegistry + function hasActiveGenerationReservation( + OperatorSet memory operatorSet + ) public view returns (bool) { + return _activeGenerationReservations.contains(operatorSet.key()); + } + /// @inheritdoc ICrossChainRegistry function getActiveGenerationReservationsByRange( uint256 startIndex, diff --git a/src/test/unit/CrossChainRegistryUnit.t.sol b/src/test/unit/CrossChainRegistryUnit.t.sol index b94b9b9c26..8c5ee5e252 100644 --- a/src/test/unit/CrossChainRegistryUnit.t.sol +++ b/src/test/unit/CrossChainRegistryUnit.t.sol @@ -1046,3 +1046,20 @@ contract CrossChainRegistryUnitTests_getActiveGenerationReservationCount is Cros assertEq(count, numReservations, "Count should match expected number"); } } + +contract CrossChainRegistryUnitTests_hasActiveGenerationReservation is CrossChainRegistryUnitTests { + function test_hasActiveGenerationReservation_Single() public { + bool hasReservation = crossChainRegistry.hasActiveGenerationReservation(defaultOperatorSet); + assertFalse(hasReservation, "Should not have any reservations"); + + crossChainRegistry.createGenerationReservation(defaultOperatorSet, defaultCalculator, defaultConfig); + + hasReservation = crossChainRegistry.hasActiveGenerationReservation(defaultOperatorSet); + assertTrue(hasReservation, "Should have a reservation"); + + crossChainRegistry.removeGenerationReservation(defaultOperatorSet); + + hasReservation = crossChainRegistry.hasActiveGenerationReservation(defaultOperatorSet); + assertFalse(hasReservation, "Should not have a reservation"); + } +} From a2e2fcd8361784f0cc1044e5082c8193b7a3f365 Mon Sep 17 00:00:00 2001 From: "clandestine.eth" <96172957+0xClandestine@users.noreply.github.com> Date: Tue, 5 Aug 2025 14:07:01 -0400 Subject: [PATCH 2/2] nit: update comment --- src/contracts/interfaces/ICrossChainRegistry.sol | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/contracts/interfaces/ICrossChainRegistry.sol b/src/contracts/interfaces/ICrossChainRegistry.sol index 540a4d685d..be46753393 100644 --- a/src/contracts/interfaces/ICrossChainRegistry.sol +++ b/src/contracts/interfaces/ICrossChainRegistry.sol @@ -280,8 +280,8 @@ interface ICrossChainRegistry is ICrossChainRegistryErrors, ICrossChainRegistryE /** * @notice Gets the operatorTableCalculator for a given operatorSet - * @dev You may want to query `hasActiveGenerationReservation` before calling this method * @param operatorSet the operatorSet to get the operatorTableCalculator for + * @dev You should check if an operatorSet has an active generation reservation prior to calling this method * @return The operatorTableCalculator for the given operatorSet */ function getOperatorTableCalculator( @@ -290,8 +290,8 @@ interface ICrossChainRegistry is ICrossChainRegistryErrors, ICrossChainRegistryE /** * @notice Gets the operatorSetConfig for a given operatorSet - * @dev You may want to query `hasActiveGenerationReservation` before calling this method. * @param operatorSet the operatorSet to get the operatorSetConfig for + * @dev You should check if an operatorSet has an active generation reservation prior to calling this method * @return The operatorSetConfig for the given operatorSet */ function getOperatorSetConfig( @@ -300,13 +300,13 @@ interface ICrossChainRegistry is ICrossChainRegistryErrors, ICrossChainRegistryE /** * @notice Calculates the operatorTableBytes for a given operatorSet - * @dev You may want to query `hasActiveGenerationReservation` before calling this method. * @param operatorSet the operatorSet to calculate the operator table for * @return the encoded operatorTableBytes containing: * - operatorSet details * - curve type from KeyRegistrar * - operator set configuration * - calculated operator table from the calculator contract + * @dev You should check if an operatorSet has an active generation reservation prior to calling this method * @dev This function aggregates data from multiple sources for cross-chain transport * @dev Reverts when the call to the operatorTableCalculator contract call fails */