@@ -17,7 +17,7 @@ use futures_util::future::join_all;
1717use hyperlane_core:: Metadata ;
1818use tokio:: join;
1919use tokio:: sync:: Mutex ;
20- use tracing:: instrument;
20+ use tracing:: { instrument, warn } ;
2121
2222use hyperlane_core:: {
2323 rpc_clients:: call_and_retry_indefinitely, BatchItem , BatchResult , ChainCommunicationError ,
@@ -341,7 +341,16 @@ where
341341 tx_gas_estimate : Option < U256 > ,
342342 with_gas_estimate_buffer : bool ,
343343 ) -> ChainResult < ContractCall < M , ( ) > > {
344- let tx = self . contract_call ( message, metadata, tx_gas_estimate) ?;
344+ let mut tx = self . contract_call ( message, metadata, tx_gas_estimate) ?;
345+
346+ // Explicitly set `from` to the relayer's signer address so that during
347+ // eth_estimateGas the simulated msg.sender matches the trusted relayer.
348+ // TrustedRelayerIsm sets deliveries[id].processor = msg.sender *before*
349+ // calling ism.verify(), so verify() checks msg.sender == trustedRelayer.
350+ // Without this, from defaults to address(0) and estimation reverts.
351+ if let Some ( sender) = self . provider . default_sender ( ) {
352+ tx = tx. from ( sender) ;
353+ }
345354
346355 fill_tx_gas_params (
347356 tx,
@@ -644,22 +653,32 @@ where
644653 . ok_or ( HyperlaneProtocolError :: ProcessGasLimitRequired ) ?;
645654
646655 // If we have a ArbitrumNodeInterface, we need to set the l2_gas_limit.
656+ // Note: estimate_retryable_ticket simulates execution via a retryable ticket, where
657+ // msg.sender is the L2 alias of the sender arg (not the actual relayer address).
658+ // For ISMs like TrustedRelayerIsm that check msg.sender, this simulation will fail.
659+ // In that case we fall back to l2_gas_limit = None so enforceable_gas_limit() uses
660+ // the direct process() gas estimate instead.
647661 let l2_gas_limit = if let Some ( arbitrum_node_interface) = & self . arbitrum_node_interface {
648- Some (
649- arbitrum_node_interface
650- . estimate_retryable_ticket (
651- H160 :: zero ( ) . into ( ) ,
652- // Give the sender a deposit (100 ETH), otherwise it reverts
653- WEI_IN_ETHER . mul ( 100u32 ) ,
654- self . contract . address ( ) ,
655- U256 :: zero ( ) . into ( ) ,
656- H160 :: zero ( ) . into ( ) ,
657- H160 :: zero ( ) . into ( ) ,
658- contract_call. calldata ( ) . unwrap_or_default ( ) ,
659- )
660- . estimate_gas ( )
661- . await ?,
662- )
662+ match arbitrum_node_interface
663+ . estimate_retryable_ticket (
664+ H160 :: zero ( ) . into ( ) ,
665+ // Give the sender a deposit (100 ETH), otherwise it reverts
666+ WEI_IN_ETHER . mul ( 100u32 ) ,
667+ self . contract . address ( ) ,
668+ U256 :: zero ( ) . into ( ) ,
669+ H160 :: zero ( ) . into ( ) ,
670+ H160 :: zero ( ) . into ( ) ,
671+ contract_call. calldata ( ) . unwrap_or_default ( ) ,
672+ )
673+ . estimate_gas ( )
674+ . await
675+ {
676+ Ok ( estimate) => Some ( estimate) ,
677+ Err ( err) => {
678+ warn ! ( ?err, "Failed to estimate l2_gas_limit via ArbitrumNodeInterface, falling back to direct gas estimate" ) ;
679+ None
680+ }
681+ }
663682 } else {
664683 None
665684 } ;
0 commit comments