@@ -31,6 +31,10 @@ import { MultiProvider } from '../../providers/MultiProvider.js';
3131import { CallData as SdkCallData } from '../../providers/transactions/types.js' ;
3232import { RouterApp } from '../../router/RouterApps.js' ;
3333import { ChainMap , ChainName } from '../../types.js' ;
34+ import {
35+ estimateCallGas ,
36+ estimateHandleGasForRecipient ,
37+ } from '../../utils/gas.js' ;
3438
3539import {
3640 InterchainAccountFactories ,
@@ -39,6 +43,8 @@ import {
3943import { AccountConfig , GetCallRemoteSettings } from './types.js' ;
4044
4145const IGP_DEFAULT_GAS = BigNumber . from ( 50_000 ) ;
46+ const ICA_OVERHEAD = BigNumber . from ( 50_000 ) ;
47+ const PER_CALL_OVERHEAD = BigNumber . from ( 5_000 ) ;
4248const ICA_HANDLE_GAS_FALLBACK = BigNumber . from ( 200_000 ) ;
4349
4450export class InterchainAccount extends RouterApp < InterchainAccountFactories > {
@@ -230,43 +236,53 @@ export class InterchainAccount extends RouterApp<InterchainAccountFactories> {
230236 ) ;
231237
232238 try {
233- const gasEstimate = await destinationRouter . estimateGas . handle (
234- originDomain ,
235- addressToBytes32 ( localRouterAddress ) ,
236- messageBody ,
237- { from : await destinationRouter . mailbox ( ) } ,
239+ const mailbox = await destinationRouter . mailbox ( ) ;
240+ const gasEstimate = await estimateHandleGasForRecipient ( {
241+ recipient : destinationRouter ,
242+ origin : originDomain ,
243+ sender : addressToBytes32 ( localRouterAddress ) ,
244+ body : messageBody ,
245+ mailbox,
246+ } ) ;
247+
248+ if ( gasEstimate ) {
249+ return addBufferToGasLimit ( gasEstimate ) ;
250+ }
251+ } catch {
252+ // Fall through to individual call estimation
253+ }
254+
255+ this . logger . warn (
256+ { destination } ,
257+ 'Failed to estimate ICA handle gas, trying individual call estimation' ,
258+ ) ;
259+
260+ try {
261+ const provider = this . multiProvider . getProvider ( destination ) ;
262+ const individualEstimates = await Promise . all (
263+ formattedCalls . map ( ( call ) =>
264+ estimateCallGas ( {
265+ provider,
266+ to : bytes32ToAddress ( call . to ) ,
267+ data : call . data ,
268+ value : call . value ,
269+ } ) ,
270+ ) ,
271+ ) ;
272+ const totalGas = individualEstimates . reduce (
273+ ( sum , gas ) => sum . add ( gas ) ,
274+ BigNumber . from ( 0 ) ,
275+ ) ;
276+ const overhead = ICA_OVERHEAD . add (
277+ PER_CALL_OVERHEAD . mul ( formattedCalls . length ) ,
238278 ) ;
239- return addBufferToGasLimit ( gasEstimate ) ;
240- } catch ( error ) {
279+ return addBufferToGasLimit ( totalGas . add ( overhead ) ) ;
280+ } catch {
241281 this . logger . warn (
242- { error , destination } ,
243- 'Failed to estimate ICA handle gas, trying individual call estimation ' ,
282+ { destination } ,
283+ 'Individual call estimation also failed, using static fallback ' ,
244284 ) ;
245-
246- try {
247- const individualEstimates = await Promise . all (
248- formattedCalls . map ( ( call ) =>
249- this . estimateIndividualCallGas ( destination , call ) ,
250- ) ,
251- ) ;
252- const totalGas = individualEstimates . reduce (
253- ( sum , gas ) => sum . add ( gas ) ,
254- BigNumber . from ( 0 ) ,
255- ) ;
256- // Overhead: ICA deployment check + multicall dispatch + message decoding
257- const ICA_OVERHEAD = BigNumber . from ( 50_000 ) ;
258- const PER_CALL_OVERHEAD = BigNumber . from ( 5_000 ) ;
259- const overhead = ICA_OVERHEAD . add (
260- PER_CALL_OVERHEAD . mul ( formattedCalls . length ) ,
261- ) ;
262- return addBufferToGasLimit ( totalGas . add ( overhead ) ) ;
263- } catch ( fallbackError ) {
264- this . logger . warn (
265- { error : fallbackError , destination } ,
266- 'Individual call estimation also failed, using static fallback' ,
267- ) ;
268- return ICA_HANDLE_GAS_FALLBACK ;
269- }
285+ return ICA_HANDLE_GAS_FALLBACK ;
270286 }
271287 }
272288
@@ -353,27 +369,6 @@ export class InterchainAccount extends RouterApp<InterchainAccountFactories> {
353369 return parsed ? BigNumber . from ( parsed . gasLimit ) : null ;
354370 }
355371
356- /**
357- * Estimate gas for a single call on the destination chain.
358- * Used as fallback when full handle() estimation fails.
359- */
360- private async estimateIndividualCallGas (
361- destination : string ,
362- call : { to : string ; value : BigNumber ; data : string } ,
363- ) : Promise < BigNumber > {
364- const provider = this . multiProvider . getProvider ( destination ) ;
365- const PER_CALL_FALLBACK = BigNumber . from ( 50_000 ) ;
366- try {
367- return await provider . estimateGas ( {
368- to : bytes32ToAddress ( call . to ) ,
369- data : call . data ,
370- value : call . value ,
371- } ) ;
372- } catch {
373- return PER_CALL_FALLBACK ;
374- }
375- }
376-
377372 // general helper for different overloaded callRemote functions
378373 // can override the gasLimit by StandardHookMetadata.overrideGasLimit for optional hookMetadata here
379374 async callRemote ( {
0 commit comments