@@ -208,6 +208,38 @@ export async function estimateGas({
208208 } ;
209209}
210210
211+ /**
212+ * Honour caller-provided gas limits without simulating.
213+ *
214+ * If every transaction in the batch already has a `gas` value, returns the
215+ * parsed per-tx limits and their sum. Otherwise returns `undefined` so the
216+ * caller falls back to simulation.
217+ *
218+ * Used by both branches of `estimateGasBatch` (EIP-7702 and non-7702) — required
219+ * for callers that submit batches whose individual sub-calls cannot be simulated
220+ * standalone (e.g. predict-withdraw, where the batch's first sub-call provides
221+ * source-token balance to subsequent sub-calls (approve + swap), and simulating
222+ * the relay-only sub-calls in isolation reverts because the caller has no
223+ * balance until the first sub-call has run).
224+ *
225+ * @param transactions - Batch transactions to inspect.
226+ * @returns Parsed gas limits and total when every transaction has gas; otherwise `undefined`.
227+ */
228+ export function getProvidedBatchGasLimits (
229+ transactions : BatchTransactionParams [ ] ,
230+ ) : { gasLimits : number [ ] ; totalGasLimit : number } | undefined {
231+ if ( ! transactions . every ( ( transaction ) => transaction . gas !== undefined ) ) {
232+ return undefined ;
233+ }
234+
235+ const gasLimits = transactions . map ( ( transaction ) =>
236+ new BigNumber ( transaction . gas as Hex ) . toNumber ( ) ,
237+ ) ;
238+ const totalGasLimit = gasLimits . reduce ( ( acc , gasLimit ) => acc + gasLimit , 0 ) ;
239+
240+ return { gasLimits, totalGasLimit } ;
241+ }
242+
211243export async function estimateGasBatch ( {
212244 from,
213245 getSimulationConfig,
@@ -245,34 +277,13 @@ export async function estimateGasBatch({
245277 }
246278
247279 if ( chainResult ) {
248- // If the caller already provided a gas limit on every transaction, trust
249- // them and skip simulation. This matches the non-7702 path's behaviour
250- // below and is essential for callers that submit batches whose individual
251- // calls cannot be simulated standalone — e.g. predict-withdraw, where the
252- // batch's first sub-call (Safe.execTransaction) provides source-token
253- // balance to subsequent sub-calls (approve + swap), and simulating the
254- // relay-only sub-calls in isolation reverts because the EOA has no
255- // balance until the Safe sub-call has run.
256- const allTransactionsHaveGas = transactions . every (
257- ( transaction ) => transaction . gas !== undefined ,
258- ) ;
259-
260- if ( allTransactionsHaveGas ) {
261- const gasLimits = transactions . map ( ( transaction ) =>
262- new BigNumber ( transaction . gas as Hex ) . toNumber ( ) ,
263- ) ;
264- const totalGasLimit = gasLimits . reduce (
265- ( acc , gasLimit ) => acc + gasLimit ,
266- 0 ,
267- ) ;
268- log ( 'Using batch parameter gas limits (7702 path)' , {
269- gasLimits,
270- totalGasLimit,
271- } ) ;
280+ const providedBatchGasLimits = getProvidedBatchGasLimits ( transactions ) ;
281+ if ( providedBatchGasLimits ) {
282+ log ( 'Using batch parameter gas limits (7702 path)' , providedBatchGasLimits ) ;
272283 return {
273- gasLimits : [ totalGasLimit ] ,
284+ gasLimits : [ providedBatchGasLimits . totalGasLimit ] ,
274285 ...( isUpgradeRequired ? { requiresAuthorizationList : true } : { } ) ,
275- totalGasLimit,
286+ totalGasLimit : providedBatchGasLimits . totalGasLimit ,
276287 } ;
277288 }
278289
@@ -310,20 +321,10 @@ export async function estimateGasBatch({
310321 } ;
311322 }
312323
313- const allTransactionsHaveGas = transactions . every (
314- ( transaction ) => transaction . gas !== undefined ,
315- ) ;
316-
317- if ( allTransactionsHaveGas ) {
318- const gasLimits = transactions . map ( ( transaction ) =>
319- new BigNumber ( transaction . gas as Hex ) . toNumber ( ) ,
320- ) ;
321-
322- const total = gasLimits . reduce ( ( acc , gasLimit ) => acc + gasLimit , 0 ) ;
323-
324- log ( 'Using batch parameter gas limits' , { gasLimits, total } ) ;
325-
326- return { totalGasLimit : total , gasLimits } ;
324+ const providedBatchGasLimits = getProvidedBatchGasLimits ( transactions ) ;
325+ if ( providedBatchGasLimits ) {
326+ log ( 'Using batch parameter gas limits' , providedBatchGasLimits ) ;
327+ return providedBatchGasLimits ;
327328 }
328329
329330 const { gasLimits : gasLimitsHex } = await simulateGasBatch ( {
0 commit comments