@@ -12,6 +12,7 @@ import (
1212 "syscall"
1313 "time"
1414
15+ "github.com/ethereum/go-ethereum/ethclient"
1516 "github.com/prometheus/client_golang/prometheus/promhttp"
1617 "github.com/spf13/cobra"
1718 "go.opentelemetry.io/otel"
@@ -65,6 +66,9 @@ func init() {
6566 rootCmd .Flags ().String ("metricsListenAddr" , "0.0.0.0:9090" , "The ip:port on which to export prometheus metrics." )
6667 rootCmd .Flags ().Bool ("ramp-up" , false , "Ramp up loadtest" )
6768 rootCmd .Flags ().String ("report-path" , "" , "Path to save the report" )
69+ rootCmd .Flags ().String ("txs-dir" , "" , "Path to save the transactions" )
70+ rootCmd .Flags ().Uint64 ("target-gas" , 10_000_000 , "Target gas per block" )
71+ rootCmd .Flags ().Int ("num-blocks-to-write" , 100 , "Number of blocks to write" )
6872
6973 // Initialize Viper with proper error handling
7074 if err := config .InitializeViper (rootCmd ); err != nil {
@@ -169,12 +173,6 @@ func runLoadTest(ctx context.Context, cmd *cobra.Command, args []string) error {
169173 sharedLimiter = rate .NewLimiter (rate .Inf , 1 )
170174 }
171175
172- // Create the sender from the config struct
173- snd , err := sender .NewShardedSender (cfg , settings .BufferSize , settings .Workers , sharedLimiter )
174- if err != nil {
175- return fmt .Errorf ("failed to create sender: %w" , err )
176- }
177-
178176 // Create and start block collector if endpoints are available
179177 var blockCollector * stats.BlockCollector
180178 if len (cfg .Endpoints ) > 0 && settings .TrackBlocks {
@@ -207,6 +205,12 @@ func runLoadTest(ctx context.Context, cmd *cobra.Command, args []string) error {
207205 })
208206 }
209207
208+ // Create the sender from the config struct
209+ snd , err := sender .NewShardedSender (cfg , settings .BufferSize , settings .Workers , sharedLimiter )
210+ if err != nil {
211+ return fmt .Errorf ("failed to create sender: %w" , err )
212+ }
213+
210214 // Enable dry-run mode in sender if specified
211215 if settings .DryRun {
212216 snd .SetDryRun (true )
@@ -225,7 +229,25 @@ func runLoadTest(ctx context.Context, cmd *cobra.Command, args []string) error {
225229 snd .SetStatsCollector (collector , logger )
226230
227231 // Create dispatcher
228- dispatcher := sender .NewDispatcher (gen , snd )
232+ var dispatcher * sender.Dispatcher
233+ if settings .TxsDir != "" {
234+ // get latest height
235+ ethclient , err := ethclient .Dial (cfg .Endpoints [0 ])
236+ if err != nil {
237+ return fmt .Errorf ("failed to create ethclient: %w" , err )
238+ }
239+ latestHeight , err := ethclient .BlockNumber (ctx )
240+ if err != nil {
241+ return fmt .Errorf ("failed to get latest height: %w" , err )
242+ }
243+ numBlocksToWrite := settings .NumBlocksToWrite
244+ writerHeight := latestHeight + 10 // some buffer
245+ log .Printf ("🔍 Latest height: %d, writer start height: %d" , latestHeight , writerHeight )
246+ writer := sender .NewTxsWriter (settings .TargetGas , settings .TxsDir , writerHeight , uint64 (numBlocksToWrite ))
247+ dispatcher = sender .NewDispatcher (gen , writer )
248+ } else {
249+ dispatcher = sender .NewDispatcher (gen , snd )
250+ }
229251
230252 // Set statistics collector for dispatcher
231253 dispatcher .SetStatsCollector (collector )
@@ -239,10 +261,11 @@ func runLoadTest(ctx context.Context, cmd *cobra.Command, args []string) error {
239261 log .Printf ("📝 Prewarm mode: Accounts will be prewarmed" )
240262 }
241263
242- // Start the sender (starts all workers)
243- s .SpawnBgNamed ("sender" , func () error { return snd .Run (ctx ) })
244- log .Printf ("✅ Connected to %d endpoints" , snd .GetNumShards ())
245-
264+ if settings .TxsDir == "" {
265+ // Start the sender (starts all workers)
266+ s .SpawnBgNamed ("sender" , func () error { return snd .Run (ctx ) })
267+ log .Printf ("✅ Connected to %d endpoints" , snd .GetNumShards ())
268+ }
246269 // Perform prewarming if enabled (before starting logger to avoid logging prewarm transactions)
247270 if settings .Prewarm {
248271 if err := dispatcher .Prewarm (ctx ); err != nil {
0 commit comments