@@ -63,7 +63,7 @@ type FeeData struct {
63
63
gasLimit uint64
64
64
}
65
65
66
- // Sender Transaction sender to send transaction to l1/l2 geth
66
+ // Sender Transaction sender to send transaction to l1/l2
67
67
type Sender struct {
68
68
config * config.SenderConfig
69
69
gethClient * gethclient.Client
@@ -105,13 +105,7 @@ func NewSender(ctx context.Context, config *config.SenderConfig, signerConfig *c
105
105
return nil , fmt .Errorf ("failed to create transaction signer, err: %w" , err )
106
106
}
107
107
108
- // Set pending nonce
109
- nonce , err := client .PendingNonceAt (ctx , transactionSigner .GetAddr ())
110
- if err != nil {
111
- return nil , fmt .Errorf ("failed to get pending nonce for address %s, err: %w" , transactionSigner .GetAddr (), err )
112
- }
113
- transactionSigner .SetNonce (nonce )
114
-
108
+ // Create sender instance first and then initialize nonce
115
109
sender := & Sender {
116
110
ctx : ctx ,
117
111
config : config ,
@@ -127,8 +121,13 @@ func NewSender(ctx context.Context, config *config.SenderConfig, signerConfig *c
127
121
service : service ,
128
122
senderType : senderType ,
129
123
}
130
- sender .metrics = initSenderMetrics (reg )
131
124
125
+ // Initialize nonce using the new method
126
+ if err := sender .resetNonce (); err != nil {
127
+ return nil , fmt .Errorf ("failed to reset nonce: %w" , err )
128
+ }
129
+
130
+ sender .metrics = initSenderMetrics (reg )
132
131
go sender .loop (ctx )
133
132
134
133
return sender , nil
@@ -242,7 +241,10 @@ func (s *Sender) SendTransaction(contextID string, target *common.Address, data
242
241
// Check if contain nonce, and reset nonce
243
242
// only reset nonce when it is not from resubmit
244
243
if strings .Contains (err .Error (), "nonce too low" ) {
245
- s .resetNonce (context .Background ())
244
+ if err := s .resetNonce (); err != nil {
245
+ log .Warn ("failed to reset nonce after failed send transaction" , "address" , s .transactionSigner .GetAddr ().String (), "err" , err )
246
+ return common.Hash {}, 0 , fmt .Errorf ("failed to reset nonce after failed send transaction, err: %w" , err )
247
+ }
246
248
}
247
249
return common.Hash {}, 0 , fmt .Errorf ("failed to send transaction, err: %w" , err )
248
250
}
@@ -327,14 +329,46 @@ func (s *Sender) createTx(feeData *FeeData, target *common.Address, data []byte,
327
329
return signedTx , nil
328
330
}
329
331
332
+ // initializeNonce initializes the nonce by taking the maximum of database nonce and pending nonce.
333
+ func (s * Sender ) initializeNonce () (uint64 , error ) {
334
+ // Get maximum nonce from database
335
+ dbNonce , err := s .pendingTransactionOrm .GetMaxNonceBySenderAddress (s .ctx , s .transactionSigner .GetAddr ().Hex ())
336
+ if err != nil {
337
+ return 0 , fmt .Errorf ("failed to get max nonce from database for address %s, err: %w" , s .transactionSigner .GetAddr ().Hex (), err )
338
+ }
339
+
340
+ // Get pending nonce from the client
341
+ pendingNonce , err := s .client .PendingNonceAt (s .ctx , s .transactionSigner .GetAddr ())
342
+ if err != nil {
343
+ return 0 , fmt .Errorf ("failed to get pending nonce for address %s, err: %w" , s .transactionSigner .GetAddr ().Hex (), err )
344
+ }
345
+
346
+ // Take the maximum of pending nonce and (db nonce + 1)
347
+ // Database stores the used nonce, so the next available nonce should be dbNonce + 1
348
+ // When dbNonce is -1 (no records), dbNonce + 1 = 0, which is correct
349
+ nextDbNonce := uint64 (dbNonce + 1 )
350
+ var finalNonce uint64
351
+ if pendingNonce > nextDbNonce {
352
+ finalNonce = pendingNonce
353
+ } else {
354
+ finalNonce = nextDbNonce
355
+ }
356
+
357
+ log .Info ("nonce initialization" , "address" , s .transactionSigner .GetAddr ().Hex (), "maxDbNonce" , dbNonce , "nextDbNonce" , nextDbNonce , "pendingNonce" , pendingNonce , "finalNonce" , finalNonce )
358
+
359
+ return finalNonce , nil
360
+ }
361
+
330
362
// resetNonce reset nonce if send signed tx failed.
331
- func (s * Sender ) resetNonce (ctx context. Context ) {
332
- nonce , err := s .client . PendingNonceAt ( ctx , s . transactionSigner . GetAddr () )
363
+ func (s * Sender ) resetNonce () error {
364
+ nonce , err := s .initializeNonce ( )
333
365
if err != nil {
334
- log .Warn ("failed to reset nonce" , "address" , s .transactionSigner .GetAddr ().String (), "err" , err )
335
- return
366
+ log .Error ("failed to reset nonce" , "address" , s .transactionSigner .GetAddr ().String (), "err" , err )
367
+ return fmt . Errorf ( "failed to reset nonce, err: %w" , err )
336
368
}
369
+ log .Info ("reset nonce" , "address" , s .transactionSigner .GetAddr ().String (), "nonce" , nonce )
337
370
s .transactionSigner .SetNonce (nonce )
371
+ return nil
338
372
}
339
373
340
374
func (s * Sender ) createReplacingTransaction (tx * gethTypes.Transaction , baseFee , blobBaseFee uint64 ) (* gethTypes.Transaction , error ) {
@@ -612,6 +646,16 @@ func (s *Sender) checkPendingTransaction() {
612
646
}
613
647
614
648
if err := s .client .SendTransaction (s .ctx , newSignedTx ); err != nil {
649
+ if strings .Contains (err .Error (), "nonce too low" ) {
650
+ // When we receive a 'nonce too low' error but cannot find the transaction receipt, it indicates another transaction with this nonce has already been processed, so this transaction will never be mined and should be marked as failed.
651
+ log .Warn ("nonce too low detected, marking all non-confirmed transactions with same nonce as failed" , "nonce" , originalTx .Nonce (), "address" , s .transactionSigner .GetAddr ().Hex (), "txHash" , originalTx .Hash ().Hex (), "newTxHash" , newSignedTx .Hash ().Hex (), "err" , err )
652
+ txHashes := []string {originalTx .Hash ().Hex (), newSignedTx .Hash ().Hex ()}
653
+ if updateErr := s .pendingTransactionOrm .UpdateTransactionStatusByTxHashes (s .ctx , txHashes , types .TxStatusConfirmedFailed ); updateErr != nil {
654
+ log .Error ("failed to update transaction status" , "hashes" , txHashes , "err" , updateErr )
655
+ return
656
+ }
657
+ return
658
+ }
615
659
// SendTransaction failed, need to rollback the previous database changes
616
660
if rollbackErr := s .db .Transaction (func (tx * gorm.DB ) error {
617
661
// Restore original transaction status back to pending
0 commit comments