@@ -31,6 +31,7 @@ import {
3131 TrustedRelayerIsm__factory ,
3232 ZKSyncArtifact ,
3333} from '@hyperlane-xyz/core' ;
34+
3435import {
3536 Address ,
3637 Domain ,
@@ -84,6 +85,31 @@ const ismFactories = {
8485 [ IsmType . CCIP ] : new CCIPIsm__factory ( ) ,
8586} ;
8687
88+ // First @hyperlane -xyz/core version with setBatch/removeBatch on
89+ // DomainRoutingIsm. Routing ISMs at or above this version let the SDK
90+ // consolidate per-domain txs into chunked setBatch/removeBatch calls;
91+ // older ISMs fall back to the per-domain loop.
92+ const ROUTING_ISM_BATCH_MIN_VERSION = { major : 11 , minor : 4 } ;
93+
94+ const routingIsmSupportsBatch = async (
95+ address : Address ,
96+ provider : ethers . providers . Provider ,
97+ ) : Promise < boolean > => {
98+ try {
99+ const version = await DomainRoutingIsm__factory . connect (
100+ address ,
101+ provider ,
102+ ) . PACKAGE_VERSION ( ) ;
103+ const [ major , minor ] = version . split ( '.' ) . map ( ( n ) => Number . parseInt ( n ) ) ;
104+ if ( ! Number . isFinite ( major ) || ! Number . isFinite ( minor ) ) return false ;
105+ if ( major > ROUTING_ISM_BATCH_MIN_VERSION . major ) return true ;
106+ if ( major < ROUTING_ISM_BATCH_MIN_VERSION . major ) return false ;
107+ return minor >= ROUTING_ISM_BATCH_MIN_VERSION . minor ;
108+ } catch {
109+ return false ;
110+ }
111+ } ;
112+
87113const domainRoutingInitializationSize = ( destination : ChainName ) => {
88114 if ( destination === 'tempo' ) {
89115 return 30 ;
@@ -541,7 +567,14 @@ export class HyperlaneIsmFactory extends HyperlaneApp<ProxyFactoryFactories> {
541567 existingIsmAddress ,
542568 this . multiProvider . getSigner ( destination ) ,
543569 ) ;
570+ const batchSize = domainRoutingInitializationSize ( destination ) ;
571+ const supportsBatch = await routingIsmSupportsBatch (
572+ existingIsmAddress ,
573+ provider ,
574+ ) ;
575+
544576 // deploying all the ISMs which have to be updated
577+ const enrollAddresses : Address [ ] = [ ] ;
545578 for ( const originDomain of delta . domainsToEnroll ) {
546579 const origin = this . multiProvider . getChainName ( originDomain ) ; // already filtered to only include domains in the multiprovider
547580 logger . debug (
@@ -554,20 +587,46 @@ export class HyperlaneIsmFactory extends HyperlaneApp<ProxyFactoryFactories> {
554587 mailbox,
555588 } ) ;
556589 isms [ originDomain ] = ism . address ;
557- const tx = await routingIsm . set (
558- originDomain ,
559- isms [ originDomain ] ,
590+ enrollAddresses . push ( ism . address ) ;
591+ }
592+ if ( supportsBatch ) {
593+ await this . setDomainsBatched (
594+ routingIsm ,
595+ delta . domainsToEnroll ,
596+ enrollAddresses ,
597+ batchSize ,
560598 overrides ,
599+ destination ,
600+ logger ,
561601 ) ;
562- await this . multiProvider . handleTx ( destination , tx ) ;
602+ } else {
603+ for ( let i = 0 ; i < delta . domainsToEnroll . length ; i ++ ) {
604+ const tx = await routingIsm . set (
605+ delta . domainsToEnroll [ i ] ,
606+ enrollAddresses [ i ] ,
607+ overrides ,
608+ ) ;
609+ await this . multiProvider . handleTx ( destination , tx ) ;
610+ }
563611 }
564612 // unenrolling domains if needed
565- for ( const originDomain of delta . domainsToUnenroll ) {
566- logger . debug (
567- `Unenrolling originDomain ${ originDomain } from preexisting routing ISM at ${ existingIsmAddress } ...` ,
613+ if ( supportsBatch && delta . domainsToUnenroll . length > 0 ) {
614+ await this . removeDomainsBatched (
615+ routingIsm ,
616+ delta . domainsToUnenroll ,
617+ batchSize ,
618+ overrides ,
619+ destination ,
620+ logger ,
568621 ) ;
569- const tx = await routingIsm . remove ( originDomain , overrides ) ;
570- await this . multiProvider . handleTx ( destination , tx ) ;
622+ } else {
623+ for ( const originDomain of delta . domainsToUnenroll ) {
624+ logger . debug (
625+ `Unenrolling originDomain ${ originDomain } from preexisting routing ISM at ${ existingIsmAddress } ...` ,
626+ ) ;
627+ const tx = await routingIsm . remove ( originDomain , overrides ) ;
628+ await this . multiProvider . handleTx ( destination , tx ) ;
629+ }
571630 }
572631 // transfer ownership if needed
573632 if ( delta . owner ) {
@@ -603,16 +662,50 @@ export class HyperlaneIsmFactory extends HyperlaneApp<ProxyFactoryFactories> {
603662 await getZKSyncArtifactByContractName ( config . type ) ,
604663 ) ;
605664 // TODO: Should verify contract here
606- logger . debug ( 'Initialising fallback routing ISM ...' ) ;
665+ const batchSize = domainRoutingInitializationSize ( destination ) ;
666+ const initialBatchSize = Math . min ( batchSize , safeConfigDomains . length ) ;
667+ const initialDomains = safeConfigDomains . slice ( 0 , initialBatchSize ) ;
668+ const initialAddresses = submoduleAddresses . slice ( 0 , initialBatchSize ) ;
669+ logger . debug (
670+ `Initialising fallback routing ISM with ${ initialBatchSize } domains on ${ destination } ` ,
671+ ) ;
607672 receipt = await this . multiProvider . handleTx (
608673 destination ,
609674 routingIsm [ 'initialize(address,uint32[],address[])' ] (
610675 config . owner ,
611- safeConfigDomains ,
612- submoduleAddresses ,
676+ initialDomains ,
677+ initialAddresses ,
613678 overrides ,
614679 ) ,
615680 ) ;
681+ const remainingDomains = safeConfigDomains . slice ( initialBatchSize ) ;
682+ const remainingAddresses = submoduleAddresses . slice ( initialBatchSize ) ;
683+ if ( remainingDomains . length > 0 ) {
684+ const supportsBatch = await routingIsmSupportsBatch (
685+ routingIsm . address ,
686+ provider ,
687+ ) ;
688+ if ( supportsBatch ) {
689+ await this . setDomainsBatched (
690+ routingIsm ,
691+ remainingDomains ,
692+ remainingAddresses ,
693+ batchSize ,
694+ overrides ,
695+ destination ,
696+ logger ,
697+ ) ;
698+ } else {
699+ for ( let i = 0 ; i < remainingDomains . length ; i ++ ) {
700+ const tx = await routingIsm . set (
701+ remainingDomains [ i ] ,
702+ remainingAddresses [ i ] ,
703+ overrides ,
704+ ) ;
705+ await this . multiProvider . handleTx ( destination , tx ) ;
706+ }
707+ }
708+ }
616709 } else {
617710 // deploying new domain routing ISM
618711 const owner = config . owner ;
@@ -704,27 +797,47 @@ export class HyperlaneIsmFactory extends HyperlaneApp<ProxyFactoryFactories> {
704797
705798 // Enroll remaining domains and addresses
706799 // If all domains are enrolled already, this is a no-op
707- for ( let i = initialBatchSize ; i < safeConfigDomains . length ; i ++ ) {
708- const estimatedGas = await routingIsm . estimateGas . set (
709- safeConfigDomains [ i ] ,
710- submoduleAddresses [ i ] ,
711- overrides ,
712- ) ;
713- const chainName = this . multiProvider . getChainName (
714- safeConfigDomains [ i ] ,
715- ) ;
716- this . logger . debug (
717- `Enrolling ${ chainName } (${ safeConfigDomains [ i ] } ) ISM at ${ submoduleAddresses [ i ] } on Domain Routing ISM ${ moduleAddress } ` ,
718- ) ;
719- const enrollTx = await routingIsm . set (
720- safeConfigDomains [ i ] ,
721- submoduleAddresses [ i ] ,
722- {
723- gasLimit : addBufferToGasLimit ( estimatedGas , 15 ) ,
724- ...overrides ,
725- } ,
800+ const remainingDomains = safeConfigDomains . slice ( initialBatchSize ) ;
801+ const remainingAddresses = submoduleAddresses . slice ( initialBatchSize ) ;
802+ if ( remainingDomains . length > 0 ) {
803+ const supportsBatch = await routingIsmSupportsBatch (
804+ moduleAddress ,
805+ provider ,
726806 ) ;
727- await this . multiProvider . handleTx ( destination , enrollTx ) ;
807+ if ( supportsBatch ) {
808+ await this . setDomainsBatched (
809+ routingIsm ,
810+ remainingDomains ,
811+ remainingAddresses ,
812+ batchSize ,
813+ overrides ,
814+ destination ,
815+ logger ,
816+ ) ;
817+ } else {
818+ for ( let i = 0 ; i < remainingDomains . length ; i ++ ) {
819+ const estimatedGas = await routingIsm . estimateGas . set (
820+ remainingDomains [ i ] ,
821+ remainingAddresses [ i ] ,
822+ overrides ,
823+ ) ;
824+ const chainName = this . multiProvider . getChainName (
825+ remainingDomains [ i ] ,
826+ ) ;
827+ this . logger . debug (
828+ `Enrolling ${ chainName } (${ remainingDomains [ i ] } ) ISM at ${ remainingAddresses [ i ] } on Domain Routing ISM ${ moduleAddress } ` ,
829+ ) ;
830+ const enrollTx = await routingIsm . set (
831+ remainingDomains [ i ] ,
832+ remainingAddresses [ i ] ,
833+ {
834+ gasLimit : addBufferToGasLimit ( estimatedGas , 15 ) ,
835+ ...overrides ,
836+ } ,
837+ ) ;
838+ await this . multiProvider . handleTx ( destination , enrollTx ) ;
839+ }
840+ }
728841 }
729842
730843 // Transfer ownership after all enrollments are complete, unless the
@@ -746,6 +859,66 @@ export class HyperlaneIsmFactory extends HyperlaneApp<ProxyFactoryFactories> {
746859 return routingIsm ;
747860 }
748861
862+ private async setDomainsBatched (
863+ routingIsm :
864+ | DomainRoutingIsm
865+ | IncrementalDomainRoutingIsm
866+ | DefaultFallbackRoutingIsm ,
867+ domains : number [ ] ,
868+ addresses : Address [ ] ,
869+ batchSize : number ,
870+ overrides : ethers . Overrides ,
871+ destination : ChainName ,
872+ logger : Logger ,
873+ ) : Promise < void > {
874+ for ( let i = 0 ; i < domains . length ; i += batchSize ) {
875+ const chunk = domains . slice ( i , i + batchSize ) . map ( ( domain , j ) => ( {
876+ domain,
877+ module : addresses [ i + j ] ,
878+ } ) ) ;
879+ logger . debug (
880+ `setBatch enrolling ${ chunk . length } domains on routing ISM ${ routingIsm . address } (${ destination } )` ,
881+ ) ;
882+ const estimatedGas = await routingIsm . estimateGas . setBatch (
883+ chunk ,
884+ overrides ,
885+ ) ;
886+ const tx = await routingIsm . setBatch ( chunk , {
887+ gasLimit : addBufferToGasLimit ( estimatedGas , 15 ) ,
888+ ...overrides ,
889+ } ) ;
890+ await this . multiProvider . handleTx ( destination , tx ) ;
891+ }
892+ }
893+
894+ private async removeDomainsBatched (
895+ routingIsm :
896+ | DomainRoutingIsm
897+ | IncrementalDomainRoutingIsm
898+ | DefaultFallbackRoutingIsm ,
899+ domains : number [ ] ,
900+ batchSize : number ,
901+ overrides : ethers . Overrides ,
902+ destination : ChainName ,
903+ logger : Logger ,
904+ ) : Promise < void > {
905+ for ( let i = 0 ; i < domains . length ; i += batchSize ) {
906+ const chunk = domains . slice ( i , i + batchSize ) ;
907+ logger . debug (
908+ `removeBatch unenrolling ${ chunk . length } domains on routing ISM ${ routingIsm . address } (${ destination } )` ,
909+ ) ;
910+ const estimatedGas = await routingIsm . estimateGas . removeBatch (
911+ chunk ,
912+ overrides ,
913+ ) ;
914+ const tx = await routingIsm . removeBatch ( chunk , {
915+ gasLimit : addBufferToGasLimit ( estimatedGas , 15 ) ,
916+ ...overrides ,
917+ } ) ;
918+ await this . multiProvider . handleTx ( destination , tx ) ;
919+ }
920+ }
921+
749922 protected async deployAggregationIsm ( params : {
750923 destination : ChainName ;
751924 config : AggregationIsmConfig ;
0 commit comments