From 1af3eb25e5c9b9bb46a0fac9585e8f5bf2fcfedd Mon Sep 17 00:00:00 2001 From: Yash Patil <40046473+ypatil12@users.noreply.github.com> Date: Mon, 9 Jun 2025 12:23:02 -0400 Subject: [PATCH 1/3] chore: add operator table updater --- .../interfaces/ICrossChainRegistry.sol | 10 +-- .../interfaces/IOperatorTableUpdater.sol | 6 +- .../multichain/CrossChainRegistry.sol | 22 +++-- .../multichain/CrossChainRegistryStorage.sol | 8 +- .../multichain/OperatorTableUpdater.sol | 10 +-- src/test/unit/CrossChainRegistryUnit.t.sol | 88 +++++++++++++++---- 6 files changed, 104 insertions(+), 40 deletions(-) diff --git a/src/contracts/interfaces/ICrossChainRegistry.sol b/src/contracts/interfaces/ICrossChainRegistry.sol index 7434163351..bb0840652f 100644 --- a/src/contracts/interfaces/ICrossChainRegistry.sol +++ b/src/contracts/interfaces/ICrossChainRegistry.sol @@ -77,7 +77,7 @@ interface ICrossChainRegistryEvents is ICrossChainRegistryTypes { event TransportDestinationRemoved(OperatorSet operatorSet, uint256 chainID); /// @notice Emitted when a chainID is added to the whitelist - event ChainIDAddedToWhitelist(uint256 chainID); + event ChainIDAddedToWhitelist(uint256 chainID, address operatorTableUpdater); /// @notice Emitted when a chainID is removed from the whitelist event ChainIDRemovedFromWhitelist(uint256 chainID); @@ -150,11 +150,10 @@ interface ICrossChainRegistry is ICrossChainRegistryErrors, ICrossChainRegistryE /** * @notice Adds chainIDs to the whitelist of chainIDs that can be transported to * @param chainIDs the chainIDs to add to the whitelist + * @param operatorTableUpdaters the operatorTableUpdaters for each whitelisted chainID * @dev msg.sender must be the owner of the CrossChainRegistry */ - function addChainIDsToWhitelist( - uint256[] calldata chainIDs - ) external; + function addChainIDsToWhitelist(uint256[] calldata chainIDs, address[] calldata operatorTableUpdaters) external; /** * @notice Removes chainIDs from the whitelist of chainIDs that can be transported to @@ -228,6 +227,7 @@ interface ICrossChainRegistry is ICrossChainRegistryErrors, ICrossChainRegistryE /** * @notice Gets the list of chains that are supported by the CrossChainRegistry * @return An array of chainIDs that are supported by the CrossChainRegistry + * @return An array of operatorTableUpdaters corresponding to each chainID */ - function getSupportedChains() external view returns (uint256[] memory); + function getSupportedChains() external view returns (uint256[] memory, address[] memory); } diff --git a/src/contracts/interfaces/IOperatorTableUpdater.sol b/src/contracts/interfaces/IOperatorTableUpdater.sol index 42bc918d8d..cbaafa9488 100644 --- a/src/contracts/interfaces/IOperatorTableUpdater.sol +++ b/src/contracts/interfaces/IOperatorTableUpdater.sol @@ -98,15 +98,15 @@ interface IOperatorTableUpdater is * @param globalTableRoot the new globalTableRoot * @param operatorSetIndex the index of the given operatorSet being updated * @param proof the proof of the leaf at index against the globalTableRoot - * @param tableInfo the tableInfo of the operator table - * @dev Depending on the decoded KeyType, the tableInfo will be decoded to a + * @param operatorTableBytes the bytes of the operator table + * @dev Depending on the decoded KeyType, the tableInfo will be decoded */ function updateOperatorTable( uint32 referenceTimestamp, bytes32 globalTableRoot, uint32 operatorSetIndex, bytes calldata proof, - bytes calldata tableInfo + bytes calldata operatorTableBytes ) external; /** diff --git a/src/contracts/multichain/CrossChainRegistry.sol b/src/contracts/multichain/CrossChainRegistry.sol index 45d0ef3cdc..69f6552b69 100644 --- a/src/contracts/multichain/CrossChainRegistry.sol +++ b/src/contracts/multichain/CrossChainRegistry.sol @@ -23,6 +23,7 @@ contract CrossChainRegistry is PermissionControllerMixin, SemVerMixin { + using EnumerableMap for EnumerableMap.UintToAddressMap; using EnumerableSet for EnumerableSet.Bytes32Set; using EnumerableSet for EnumerableSet.UintSet; using OperatorSetLib for OperatorSet; @@ -205,7 +206,8 @@ contract CrossChainRegistry is /// @inheritdoc ICrossChainRegistry function addChainIDsToWhitelist( - uint256[] calldata chainIDs + uint256[] calldata chainIDs, + address[] calldata operatorTableUpdaters ) external onlyOwner onlyWhenNotPaused(PAUSED_CHAIN_WHITELIST) { for (uint256 i = 0; i < chainIDs.length; i++) { uint256 chainID = chainIDs[i]; @@ -214,9 +216,9 @@ contract CrossChainRegistry is require(chainID != 0, InvalidChainId()); // Add to whitelist - require(_whitelistedChainIDs.add(chainID), ChainIDAlreadyWhitelisted()); + require(_whitelistedChainIDs.set(chainID, operatorTableUpdaters[i]), ChainIDAlreadyWhitelisted()); - emit ChainIDAddedToWhitelist(chainID); + emit ChainIDAddedToWhitelist(chainID, operatorTableUpdaters[i]); } } @@ -438,7 +440,17 @@ contract CrossChainRegistry is } /// @inheritdoc ICrossChainRegistry - function getSupportedChains() external view returns (uint256[] memory) { - return _whitelistedChainIDs.values(); + function getSupportedChains() external view returns (uint256[] memory, address[] memory) { + uint256 length = _whitelistedChainIDs.length(); + uint256[] memory chainIDs = new uint256[](length); + address[] memory operatorTableUpdaters = new address[](length); + + for (uint256 i = 0; i < length; i++) { + (uint256 chainID, address operatorTableUpdater) = _whitelistedChainIDs.at(i); + chainIDs[i] = chainID; + operatorTableUpdaters[i] = operatorTableUpdater; + } + + return (chainIDs, operatorTableUpdaters); } } diff --git a/src/contracts/multichain/CrossChainRegistryStorage.sol b/src/contracts/multichain/CrossChainRegistryStorage.sol index 07fef385bf..23587513fc 100644 --- a/src/contracts/multichain/CrossChainRegistryStorage.sol +++ b/src/contracts/multichain/CrossChainRegistryStorage.sol @@ -2,6 +2,7 @@ pragma solidity ^0.8.27; import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; +import "@openzeppelin/contracts/utils/structs/EnumerableMap.sol"; import "../interfaces/ICrossChainRegistry.sol"; import "../interfaces/IOperatorTableCalculator.sol"; import "../interfaces/IAllocationManager.sol"; @@ -15,6 +16,7 @@ import "../libraries/OperatorSetLib.sol"; * @dev This abstract contract is designed to be inherited by the CrossChainRegistry implementation */ abstract contract CrossChainRegistryStorage is ICrossChainRegistry { + using EnumerableMap for EnumerableMap.UintToAddressMap; using EnumerableSet for EnumerableSet.Bytes32Set; using EnumerableSet for EnumerableSet.UintSet; using OperatorSetLib for OperatorSet; @@ -62,8 +64,8 @@ abstract contract CrossChainRegistryStorage is ICrossChainRegistry { /// CHAIN WHITELISTING - /// @dev Set of whitelisted chain IDs that can be used as transport destinations - EnumerableSet.UintSet internal _whitelistedChainIDs; + /// @dev Map of whitelisted chain IDs to operator table updaters + EnumerableMap.UintToAddressMap internal _whitelistedChainIDs; // Construction @@ -77,5 +79,5 @@ abstract contract CrossChainRegistryStorage is ICrossChainRegistry { * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ - uint256[43] private __gap; + uint256[42] private __gap; } diff --git a/src/contracts/multichain/OperatorTableUpdater.sol b/src/contracts/multichain/OperatorTableUpdater.sol index fcb9e1bc66..e926ce55a5 100644 --- a/src/contracts/multichain/OperatorTableUpdater.sol +++ b/src/contracts/multichain/OperatorTableUpdater.sol @@ -89,10 +89,10 @@ contract OperatorTableUpdater is Initializable, OwnableUpgradeable, OperatorTabl bytes32 globalTableRoot, uint32 operatorSetIndex, bytes calldata proof, - bytes calldata tableInfo + bytes calldata operatorTableBytes ) external { (OperatorSet memory operatorSet, CurveType curveType, OperatorSetConfig memory operatorSetConfig) = - _getOperatorTableInfo(tableInfo); + _getOperatorTableInfo(operatorTableBytes); // Check that the `referenceTimestamp` is greater than the latest reference timestamp require( @@ -107,17 +107,17 @@ contract OperatorTableUpdater is Initializable, OwnableUpgradeable, OperatorTabl globalTableRoot: globalTableRoot, operatorSetIndex: operatorSetIndex, proof: proof, - operatorSetLeafHash: keccak256(tableInfo) + operatorSetLeafHash: keccak256(operatorTableBytes) }); // Update the operator table if (curveType == CurveType.BN254) { bn254CertificateVerifier.updateOperatorTable( - operatorSet, referenceTimestamp, _getBN254OperatorSetInfo(tableInfo), operatorSetConfig + operatorSet, referenceTimestamp, _getBN254OperatorSetInfo(operatorTableBytes), operatorSetConfig ); } else if (curveType == CurveType.ECDSA) { ecdsaCertificateVerifier.updateOperatorTable( - operatorSet, referenceTimestamp, _getECDSAOperatorSetInfo(tableInfo), operatorSetConfig + operatorSet, referenceTimestamp, _getECDSAOperatorSetInfo(operatorTableBytes), operatorSetConfig ); } else { revert InvalidCurveType(); diff --git a/src/test/unit/CrossChainRegistryUnit.t.sol b/src/test/unit/CrossChainRegistryUnit.t.sol index 2b63efd37d..5c15ffe792 100644 --- a/src/test/unit/CrossChainRegistryUnit.t.sol +++ b/src/test/unit/CrossChainRegistryUnit.t.sol @@ -31,7 +31,9 @@ contract CrossChainRegistryUnitTests is OperatorSet defaultOperatorSet; OperatorTableCalculatorMock defaultCalculator; OperatorSetConfig defaultConfig; + address defaultOperatorTableUpdater = address(0x1); uint[] defaultChainIDs; + address[] defaultOperatorTableUpdaters; uint[] emptyChainIDs; function setUp() public virtual override { @@ -47,6 +49,9 @@ contract CrossChainRegistryUnitTests is defaultChainIDs = new uint[](2); defaultChainIDs[0] = 1; defaultChainIDs[1] = 10; + defaultOperatorTableUpdaters = new address[](2); + defaultOperatorTableUpdaters[0] = defaultOperatorTableUpdater; + defaultOperatorTableUpdaters[1] = defaultOperatorTableUpdater; // Setup default permissions _grantUAMRole(address(this), defaultAVS); @@ -55,7 +60,7 @@ contract CrossChainRegistryUnitTests is allocationManagerMock.setIsOperatorSet(defaultOperatorSet, true); // Whitelist chain IDs - crossChainRegistry.addChainIDsToWhitelist(defaultChainIDs); + crossChainRegistry.addChainIDsToWhitelist(defaultChainIDs, defaultOperatorTableUpdaters); } // Helper functions @@ -90,10 +95,12 @@ contract CrossChainRegistryUnitTests is function _createAndWhitelistChainIDs(uint count) internal returns (uint[] memory) { uint[] memory chainIDs = new uint[](count); + address[] memory operatorTableUpdaters = new address[](count); for (uint i = 0; i < count; i++) { chainIDs[i] = 100 + i; + operatorTableUpdaters[i] = address(uint160(uint(100 + i))); } - crossChainRegistry.addChainIDsToWhitelist(chainIDs); + crossChainRegistry.addChainIDsToWhitelist(chainIDs, operatorTableUpdaters); return chainIDs; } } @@ -506,6 +513,7 @@ contract CrossChainRegistryUnitTests_setOperatorSetConfig is CrossChainRegistryU */ contract CrossChainRegistryUnitTests_addTransportDestinations is CrossChainRegistryUnitTests { uint[] newChainIDs; + address[] newOperatorTableUpdaters; function setUp() public override { super.setUp(); @@ -516,7 +524,10 @@ contract CrossChainRegistryUnitTests_addTransportDestinations is CrossChainRegis newChainIDs = new uint[](2); newChainIDs[0] = 20; newChainIDs[1] = 30; - crossChainRegistry.addChainIDsToWhitelist(newChainIDs); + newOperatorTableUpdaters = new address[](2); + newOperatorTableUpdaters[0] = defaultOperatorTableUpdater; + newOperatorTableUpdaters[1] = defaultOperatorTableUpdater; + crossChainRegistry.addChainIDsToWhitelist(newChainIDs, newOperatorTableUpdaters); } function test_Revert_Paused() public { @@ -629,7 +640,10 @@ contract CrossChainRegistryUnitTests_removeTransportDestinations is CrossChainRe uint[] memory newChainIDs = new uint[](2); newChainIDs[0] = 20; newChainIDs[1] = 30; - crossChainRegistry.addChainIDsToWhitelist(newChainIDs); + address[] memory newOperatorTableUpdaters = new address[](2); + newOperatorTableUpdaters[0] = defaultOperatorTableUpdater; + newOperatorTableUpdaters[1] = defaultOperatorTableUpdater; + crossChainRegistry.addChainIDsToWhitelist(newChainIDs, newOperatorTableUpdaters); crossChainRegistry.createGenerationReservation(defaultOperatorSet, defaultCalculator, defaultConfig, manyChainIDs); @@ -744,6 +758,7 @@ contract CrossChainRegistryUnitTests_removeTransportDestinations is CrossChainRe */ contract CrossChainRegistryUnitTests_addChainIDsToWhitelist is CrossChainRegistryUnitTests { uint[] newChainIDs; + address[] newOperatorTableUpdaters; function setUp() public override { super.setUp(); @@ -751,12 +766,16 @@ contract CrossChainRegistryUnitTests_addChainIDsToWhitelist is CrossChainRegistr newChainIDs[0] = 100; newChainIDs[1] = 200; newChainIDs[2] = 300; + newOperatorTableUpdaters = new address[](3); + newOperatorTableUpdaters[0] = defaultOperatorTableUpdater; + newOperatorTableUpdaters[1] = defaultOperatorTableUpdater; + newOperatorTableUpdaters[2] = defaultOperatorTableUpdater; } function test_Revert_NotOwner() public { cheats.prank(notPermissioned); cheats.expectRevert("Ownable: caller is not the owner"); - crossChainRegistry.addChainIDsToWhitelist(newChainIDs); + crossChainRegistry.addChainIDsToWhitelist(newChainIDs, newOperatorTableUpdaters); } function test_Revert_Paused() public { @@ -764,7 +783,7 @@ contract CrossChainRegistryUnitTests_addChainIDsToWhitelist is CrossChainRegistr crossChainRegistry.pause(1 << PAUSED_CHAIN_WHITELIST); cheats.expectRevert(IPausable.CurrentlyPaused.selector); - crossChainRegistry.addChainIDsToWhitelist(newChainIDs); + crossChainRegistry.addChainIDsToWhitelist(newChainIDs, newOperatorTableUpdaters); } function test_Revert_InvalidChainId() public { @@ -772,41 +791,63 @@ contract CrossChainRegistryUnitTests_addChainIDsToWhitelist is CrossChainRegistr invalidChainIDs[0] = 0; cheats.expectRevert(InvalidChainId.selector); - crossChainRegistry.addChainIDsToWhitelist(invalidChainIDs); + crossChainRegistry.addChainIDsToWhitelist(invalidChainIDs, newOperatorTableUpdaters); } function test_Revert_ChainIDAlreadyWhitelisted() public { cheats.expectRevert(ChainIDAlreadyWhitelisted.selector); - crossChainRegistry.addChainIDsToWhitelist(defaultChainIDs); + crossChainRegistry.addChainIDsToWhitelist(defaultChainIDs, newOperatorTableUpdaters); } function test_addChainIDsToWhitelist_Success() public { // Expect events for (uint i = 0; i < newChainIDs.length; i++) { cheats.expectEmit(true, true, true, true, address(crossChainRegistry)); - emit ChainIDAddedToWhitelist(newChainIDs[i]); + emit ChainIDAddedToWhitelist(newChainIDs[i], newOperatorTableUpdaters[i]); } // Add to whitelist - crossChainRegistry.addChainIDsToWhitelist(newChainIDs); + crossChainRegistry.addChainIDsToWhitelist(newChainIDs, newOperatorTableUpdaters); // Verify state - uint[] memory supportedChains = crossChainRegistry.getSupportedChains(); + (uint[] memory supportedChains, address[] memory operatorTableUpdaters) = crossChainRegistry.getSupportedChains(); assertEq(supportedChains.length, defaultChainIDs.length + newChainIDs.length, "Supported chains count mismatch"); + assertEq(operatorTableUpdaters.length, defaultChainIDs.length + newChainIDs.length, "Operator table updaters count mismatch"); + for (uint i = 0; i < supportedChains.length; i++) { + if (i < defaultChainIDs.length) { + assertEq(supportedChains[i], defaultChainIDs[i], "Supported chain mismatch"); + assertEq(operatorTableUpdaters[i], defaultOperatorTableUpdaters[i], "Operator table updater mismatch"); + } else { + assertEq(supportedChains[i], newChainIDs[i - defaultChainIDs.length], "Supported chain mismatch"); + assertEq(operatorTableUpdaters[i], newOperatorTableUpdaters[i - defaultChainIDs.length], "Operator table updater mismatch"); + } + } } function testFuzz_addChainIDsToWhitelist_MultipleChainIDs(uint8 numChainIDs) public { numChainIDs = uint8(bound(numChainIDs, 1, 50)); uint[] memory fuzzChainIDs = new uint[](numChainIDs); + address[] memory fuzzOperatorTableUpdaters = new address[](numChainIDs); for (uint i = 0; i < numChainIDs; i++) { fuzzChainIDs[i] = 1000 + uint(i); + fuzzOperatorTableUpdaters[i] = address(uint160(uint(1000 + i))); } - crossChainRegistry.addChainIDsToWhitelist(fuzzChainIDs); + crossChainRegistry.addChainIDsToWhitelist(fuzzChainIDs, fuzzOperatorTableUpdaters); - uint[] memory supportedChains = crossChainRegistry.getSupportedChains(); - assertTrue(supportedChains.length >= numChainIDs, "Not all chains added"); + (uint[] memory supportedChains, address[] memory operatorTableUpdaters) = crossChainRegistry.getSupportedChains(); + assertEq(supportedChains.length, numChainIDs + defaultChainIDs.length, "Supported chains count mismatch"); + assertEq(operatorTableUpdaters.length, numChainIDs + defaultChainIDs.length, "Operator table updaters count mismatch"); + for (uint i = 0; i < supportedChains.length; i++) { + if (i < defaultChainIDs.length) { + assertEq(supportedChains[i], defaultChainIDs[i], "Supported chain mismatch"); + assertEq(operatorTableUpdaters[i], defaultOperatorTableUpdaters[i], "Operator table updater mismatch"); + } else { + assertEq(supportedChains[i], fuzzChainIDs[i - defaultChainIDs.length], "Supported chain mismatch"); + assertEq(operatorTableUpdaters[i], fuzzOperatorTableUpdaters[i - defaultChainIDs.length], "Operator table updater mismatch"); + } + } } } @@ -852,8 +893,9 @@ contract CrossChainRegistryUnitTests_removeChainIDsFromWhitelist is CrossChainRe crossChainRegistry.removeChainIDsFromWhitelist(defaultChainIDs); // Verify state - uint[] memory supportedChains = crossChainRegistry.getSupportedChains(); + (uint[] memory supportedChains, address[] memory operatorTableUpdaters) = crossChainRegistry.getSupportedChains(); assertEq(supportedChains.length, 0, "Should have no supported chains"); + assertEq(operatorTableUpdaters.length, 0, "Should have no operator table updaters"); } function test_removeChainIDsFromWhitelist_AffectsTransportDestinations() public { @@ -988,11 +1030,13 @@ contract CrossChainRegistryUnitTests_getActiveTransportReservations is CrossChai // Create unique chain IDs for each iteration uint[] memory chainIDs = new uint[](i + 1); + address[] memory operatorTableUpdaters = new address[](i + 1); for (uint j = 0; j <= i; j++) { // Use a formula that ensures unique chainIDs across iterations chainIDs[j] = 100 + uint(i * 10 + j); + operatorTableUpdaters[j] = address(uint160(uint(100 + i * 10 + j))); } - crossChainRegistry.addChainIDsToWhitelist(chainIDs); + crossChainRegistry.addChainIDsToWhitelist(chainIDs, operatorTableUpdaters); crossChainRegistry.createGenerationReservation(operatorSet, defaultCalculator, defaultConfig, chainIDs); } @@ -1012,8 +1056,9 @@ contract CrossChainRegistryUnitTests_getActiveTransportReservations is CrossChai */ contract CrossChainRegistryUnitTests_getSupportedChains is CrossChainRegistryUnitTests { function test_getSupportedChains_Initial() public { - uint[] memory supportedChains = crossChainRegistry.getSupportedChains(); + (uint[] memory supportedChains, address[] memory operatorTableUpdaters) = crossChainRegistry.getSupportedChains(); assertEq(supportedChains.length, defaultChainIDs.length, "Should have default chains"); + assertEq(operatorTableUpdaters.length, defaultChainIDs.length, "Should have default operator table updaters"); for (uint i = 0; i < supportedChains.length; i++) { bool found = false; for (uint j = 0; j < defaultChainIDs.length; j++) { @@ -1033,7 +1078,7 @@ contract CrossChainRegistryUnitTests_getSupportedChains is CrossChainRegistryUni // Add chains uint[] memory newChains = _createAndWhitelistChainIDs(numToAdd); - uint[] memory supportedChains = crossChainRegistry.getSupportedChains(); + (uint[] memory supportedChains, address[] memory operatorTableUpdaters) = crossChainRegistry.getSupportedChains(); assertEq(supportedChains.length, defaultChainIDs.length + numToAdd, "Chain count after add mismatch"); // Remove some default chains @@ -1044,8 +1089,13 @@ contract CrossChainRegistryUnitTests_getSupportedChains is CrossChainRegistryUni } crossChainRegistry.removeChainIDsFromWhitelist(chainsToRemove); - supportedChains = crossChainRegistry.getSupportedChains(); + (supportedChains, operatorTableUpdaters) = crossChainRegistry.getSupportedChains(); assertEq(supportedChains.length, defaultChainIDs.length + numToAdd - numToRemove, "Chain count after remove mismatch"); + assertEq( + operatorTableUpdaters.length, + defaultChainIDs.length + numToAdd - numToRemove, + "Operator table updater count after remove mismatch" + ); } } } From a397e2f85e6d04dc3815e49afe6fc668d1c148ae Mon Sep 17 00:00:00 2001 From: Yash Patil <40046473+ypatil12@users.noreply.github.com> Date: Mon, 9 Jun 2025 12:39:15 -0400 Subject: [PATCH 2/3] chore: add array length validation --- src/contracts/interfaces/ICrossChainRegistry.sol | 3 +++ src/contracts/multichain/CrossChainRegistry.sol | 2 ++ src/test/unit/CrossChainRegistryUnit.t.sol | 13 +++++++++++-- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/contracts/interfaces/ICrossChainRegistry.sol b/src/contracts/interfaces/ICrossChainRegistry.sol index bb0840652f..9f00614c7b 100644 --- a/src/contracts/interfaces/ICrossChainRegistry.sol +++ b/src/contracts/interfaces/ICrossChainRegistry.sol @@ -43,6 +43,9 @@ interface ICrossChainRegistryErrors { /// @notice Thrown when the storage is not cleared error NeedToDelete(); + + /// @notice Thrown when the lenghts between two arrays are not the same + error ArrayLengthMismatch(); } interface ICrossChainRegistryTypes { diff --git a/src/contracts/multichain/CrossChainRegistry.sol b/src/contracts/multichain/CrossChainRegistry.sol index 69f6552b69..f5ebf0c834 100644 --- a/src/contracts/multichain/CrossChainRegistry.sol +++ b/src/contracts/multichain/CrossChainRegistry.sol @@ -209,6 +209,8 @@ contract CrossChainRegistry is uint256[] calldata chainIDs, address[] calldata operatorTableUpdaters ) external onlyOwner onlyWhenNotPaused(PAUSED_CHAIN_WHITELIST) { + require(chainIDs.length == operatorTableUpdaters.length, ArrayLengthMismatch()); + for (uint256 i = 0; i < chainIDs.length; i++) { uint256 chainID = chainIDs[i]; diff --git a/src/test/unit/CrossChainRegistryUnit.t.sol b/src/test/unit/CrossChainRegistryUnit.t.sol index 5c15ffe792..9e205e00df 100644 --- a/src/test/unit/CrossChainRegistryUnit.t.sol +++ b/src/test/unit/CrossChainRegistryUnit.t.sol @@ -786,17 +786,26 @@ contract CrossChainRegistryUnitTests_addChainIDsToWhitelist is CrossChainRegistr crossChainRegistry.addChainIDsToWhitelist(newChainIDs, newOperatorTableUpdaters); } + function test_Revert_ArrayLengthMismatch() public { + address[] memory invalidOperatorTableUpdaters = new address[](1); + invalidOperatorTableUpdaters[0] = defaultOperatorTableUpdater; + cheats.expectRevert(ArrayLengthMismatch.selector); + crossChainRegistry.addChainIDsToWhitelist(newChainIDs, invalidOperatorTableUpdaters); + } + function test_Revert_InvalidChainId() public { uint[] memory invalidChainIDs = new uint[](1); invalidChainIDs[0] = 0; + address[] memory invalidOperatorTableUpdaters = new address[](1); + invalidOperatorTableUpdaters[0] = defaultOperatorTableUpdater; cheats.expectRevert(InvalidChainId.selector); - crossChainRegistry.addChainIDsToWhitelist(invalidChainIDs, newOperatorTableUpdaters); + crossChainRegistry.addChainIDsToWhitelist(invalidChainIDs, invalidOperatorTableUpdaters); } function test_Revert_ChainIDAlreadyWhitelisted() public { cheats.expectRevert(ChainIDAlreadyWhitelisted.selector); - crossChainRegistry.addChainIDsToWhitelist(defaultChainIDs, newOperatorTableUpdaters); + crossChainRegistry.addChainIDsToWhitelist(defaultChainIDs, defaultOperatorTableUpdaters); } function test_addChainIDsToWhitelist_Success() public { From 3e372b8bf542fd387cbc75070af4178d9ac414da Mon Sep 17 00:00:00 2001 From: Yash Patil <40046473+ypatil12@users.noreply.github.com> Date: Mon, 9 Jun 2025 12:40:35 -0400 Subject: [PATCH 3/3] chore: typo --- src/contracts/interfaces/ICrossChainRegistry.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/contracts/interfaces/ICrossChainRegistry.sol b/src/contracts/interfaces/ICrossChainRegistry.sol index 9f00614c7b..9f78f6000b 100644 --- a/src/contracts/interfaces/ICrossChainRegistry.sol +++ b/src/contracts/interfaces/ICrossChainRegistry.sol @@ -44,7 +44,7 @@ interface ICrossChainRegistryErrors { /// @notice Thrown when the storage is not cleared error NeedToDelete(); - /// @notice Thrown when the lenghts between two arrays are not the same + /// @notice Thrown when the lengths between two arrays are not the same error ArrayLengthMismatch(); }