Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/routing-ism-batch-ops.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@hyperlane-xyz/core': minor
---

Added `setBatch(DomainModule[])` and `removeBatch(uint32[])` to `DomainRoutingIsm` so routing ISM owners can enroll or unenroll many domains in a single transaction after initialization. Mirrors the `setHooks(HookConfig[])` pattern on `DomainRoutingHook`. Inherited by `IncrementalDomainRoutingIsm` (where `removeBatch` reverts, consistent with `remove`) and `DefaultFallbackRoutingIsm`.
28 changes: 28 additions & 0 deletions solidity/contracts/isms/routing/DomainRoutingIsm.sol
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ contract DomainRoutingIsm is
// ============ Mutable Storage ============
EnumerableMapExtended.UintToBytes32Map internal _modules;

// ============ Structs ============
struct DomainModule {
uint32 domain;
IInterchainSecurityModule module;
}

// ============ External Functions ============

/**
Expand Down Expand Up @@ -74,6 +80,18 @@ contract DomainRoutingIsm is
_set(_domain, address(_module));
}

/**
* @notice Sets the ISMs to be used for the specified origin domains
* @param _domainModules The origin domains and ISMs to enroll
*/
function setBatch(
DomainModule[] calldata _domainModules
) external onlyOwner {
for (uint256 i = 0; i < _domainModules.length; ++i) {
_set(_domainModules[i].domain, address(_domainModules[i].module));
}
}

/**
* @notice Removes the specified origin domain
* @param _domain The origin domain
Expand All @@ -82,6 +100,16 @@ contract DomainRoutingIsm is
_remove(_domain);
}

/**
* @notice Removes the specified origin domains
* @param _domains The origin domains to remove
*/
function removeBatch(uint32[] calldata _domains) external onlyOwner {
for (uint256 i = 0; i < _domains.length; ++i) {
_remove(_domains[i]);
}
}

function domains() external view returns (uint256[] memory) {
return _modules.keys();
}
Expand Down
86 changes: 86 additions & 0 deletions solidity/test/isms/DomainRoutingIsm.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,76 @@ contract DomainRoutingIsmTest is Test {
assertEq(address(ism.module(domain)), address(_ism));
}

function buildDomainModules(
uint32 domain,
uint8 count
) internal returns (DomainRoutingIsm.DomainModule[] memory) {
DomainRoutingIsm.DomainModule[]
memory domainModules = new DomainRoutingIsm.DomainModule[](count);
for (uint32 i = 0; i < count; ++i) {
unchecked {
domainModules[i] = DomainRoutingIsm.DomainModule({
domain: domain + i,
module: IInterchainSecurityModule(
address(deployTestIsm(bytes32(0)))
)
});
}
}
return domainModules;
}

function testSetBatch(uint32 domain, uint8 count) public {
vm.assume(count > 0);
DomainRoutingIsm.DomainModule[]
memory domainModules = buildDomainModules(domain, count);

ism.setBatch(domainModules);
for (uint256 i = 0; i < count; ++i) {
assertEq(
address(ism.module(domainModules[i].domain)),
address(domainModules[i].module)
);
}
}

function testSetBatchNonOwner(uint32 domain) public {
DomainRoutingIsm.DomainModule[]
memory domainModules = new DomainRoutingIsm.DomainModule[](1);
domainModules[0] = DomainRoutingIsm.DomainModule({
domain: domain,
module: IInterchainSecurityModule(address(0))
});
vm.prank(NON_OWNER);
vm.expectRevert("Ownable: caller is not the owner");
ism.setBatch(domainModules);
}

function testRemoveBatch(uint32 domain, uint8 count) public virtual {
vm.assume(count > 0);
DomainRoutingIsm.DomainModule[]
memory domainModules = buildDomainModules(domain, count);
ism.setBatch(domainModules);

uint32[] memory domains = new uint32[](count);
for (uint256 i = 0; i < count; ++i) {
domains[i] = domainModules[i].domain;
}
ism.removeBatch(domains);
for (uint256 i = 0; i < count; ++i) {
vm.expectRevert();
ism.module(domains[i]);
}
}

function testRemoveBatchNonOwner(uint32 domain) public {
uint32[] memory domains = new uint32[](1);
domains[0] = domain;
vm.prank(NON_OWNER);
vm.expectRevert("Ownable: caller is not the owner");
ism.removeBatch(domains);
}

function testRemove(uint32 domain) public virtual {
vm.expectRevert();
ism.remove(domain);
Expand Down Expand Up @@ -123,6 +193,22 @@ contract DefaultFallbackRoutingIsmTest is DomainRoutingIsmTest {
new DefaultFallbackRoutingIsm(address(0));
}

function testRemoveBatch(uint32 domain, uint8 count) public override {
vm.assume(count > 0);
DomainRoutingIsm.DomainModule[]
memory domainModules = buildDomainModules(domain, count);
ism.setBatch(domainModules);

uint32[] memory domains = new uint32[](count);
for (uint256 i = 0; i < count; ++i) {
domains[i] = domainModules[i].domain;
}
ism.removeBatch(domains);
for (uint256 i = 0; i < count; ++i) {
assertEq(address(ism.module(domains[i])), address(defaultIsm));
}
}

function testVerifyNoIsm(uint32 domain, bytes32 seed) public override {
vm.assume(domain > 0);
ism.set(domain, deployTestIsm(seed));
Expand Down
13 changes: 13 additions & 0 deletions solidity/test/isms/IncrementalDomainRoutingIsm.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import "forge-std/Test.sol";

import {IncrementalDomainRoutingIsm} from "../../contracts/isms/routing/IncrementalDomainRoutingIsm.sol";
import {IncrementalDomainRoutingIsmFactory} from "../../contracts/isms/routing/IncrementalDomainRoutingIsmFactory.sol";
import {DomainRoutingIsm} from "../../contracts/isms/routing/DomainRoutingIsm.sol";
import {DomainRoutingIsmTest} from "./DomainRoutingIsm.t.sol";
import {IInterchainSecurityModule} from "../../contracts/interfaces/IInterchainSecurityModule.sol";
import {TestIsm} from "./IsmTestUtils.sol";
Expand All @@ -29,6 +30,18 @@ contract IncrementalDomainRoutingIsmTest is DomainRoutingIsmTest {
ism.remove(domain);
}

function testRemoveBatch(uint32 domain, uint8 count) public override {
vm.assume(count > 0);
uint32[] memory domains = new uint32[](count);
for (uint32 i = 0; i < count; ++i) {
unchecked {
domains[i] = domain + i;
}
}
vm.expectRevert("IncrementalDomainRoutingIsm: removal not supported");
ism.removeBatch(domains);
}

function testSetTwiceReverts(uint32 domain) public {
TestIsm _ism = deployTestIsm(bytes32(0));
TestIsm _ism2 = deployTestIsm(bytes32(uint256(1)));
Expand Down
Loading