@@ -94,6 +94,78 @@ describe('L1TxUtils', () => {
9494 } ) ;
9595 } ) ;
9696
97+ it ( 'regression: speed-up of blob tx via L1TxUtils sets non-zero maxFeePerBlobGas' , async ( ) => {
98+ await cheatCodes . setAutomine ( false ) ;
99+ await cheatCodes . setIntervalMining ( 0 ) ;
100+
101+ const baseUtils = createL1TxUtilsFromViemWallet ( l1Client , logger , dateProvider , {
102+ gasLimitBufferPercentage : 20 ,
103+ maxGwei : 500n ,
104+ maxAttempts : 1 ,
105+ checkIntervalMs : 50 ,
106+ stallTimeMs : 300 ,
107+ } ) ;
108+
109+ const blobData = new Uint8Array ( 131072 ) . fill ( 1 ) ;
110+ const kzg = Blob . getViemKzgInstance ( ) ;
111+
112+ const request = {
113+ to : '0x1234567890123456789012345678901234567890' as `0x${string } `,
114+ data : '0x' as `0x${string } `,
115+ value : 0n ,
116+ } as const ;
117+
118+ const estimatedGas = await l1Client . estimateGas ( request ) ;
119+
120+ // Send initial blob tx with a valid maxFeePerBlobGas
121+ const { txHash } = await baseUtils . sendTransaction ( request , undefined , {
122+ blobs : [ blobData ] ,
123+ kzg,
124+ maxFeePerBlobGas : 10n * WEI_CONST ,
125+ } ) ;
126+
127+ // Capture the replacement tx when it is being signed
128+ const originalSign = l1Client . signTransaction ;
129+ const signedTxs : TransactionSerializable [ ] = [ ] ;
130+ using _spy = jest . spyOn ( l1Client , 'signTransaction' ) . mockImplementation ( ( arg : any ) => {
131+ signedTxs . push ( arg ) ;
132+ return originalSign ( arg ) ;
133+ } ) ;
134+
135+ // Trigger monitor with blob inputs but WITHOUT maxFeePerBlobGas so the bug manifests
136+ const monitorPromise = baseUtils . monitorTransaction (
137+ request ,
138+ txHash ,
139+ new Set ( ) ,
140+ { gasLimit : estimatedGas } ,
141+ undefined ,
142+ {
143+ blobs : [ blobData ] ,
144+ kzg,
145+ } ,
146+ ) ;
147+
148+ // Wait until a speed-up is attempted
149+ await retryUntil (
150+ ( ) => baseUtils [ 'state' ] === TxUtilsState . SPEED_UP || signedTxs . length > 0 ,
151+ 'waiting for speed-up' ,
152+ 40 ,
153+ 0.05 ,
154+ ) ;
155+
156+ // Interrupt to stop the monitor loop and avoid hanging the test
157+ baseUtils . interrupt ( ) ;
158+ await expect ( monitorPromise ) . rejects . toThrow ( ) ;
159+
160+ // Ensure we captured a replacement tx being signed
161+ expect ( signedTxs . length ) . toBeGreaterThan ( 0 ) ;
162+ const replacement = signedTxs [ signedTxs . length - 1 ] as any ;
163+
164+ // Assert fix: maxFeePerBlobGas is populated and non-zero on replacement
165+ expect ( replacement . maxFeePerBlobGas ) . toBeDefined ( ) ;
166+ expect ( replacement . maxFeePerBlobGas ! ) . toBeGreaterThan ( 0n ) ;
167+ } , 20_000 ) ;
168+
97169 it ( 'sends and monitors a simple transaction' , async ( ) => {
98170 const { receipt } = await gasUtils . sendAndMonitorTransaction ( {
99171 to : '0x1234567890123456789012345678901234567890' ,
0 commit comments