From 016ee8b8059e511580aba74f7043564f1581e41c Mon Sep 17 00:00:00 2001 From: Alok Nerurkar Date: Sat, 18 Oct 2025 20:06:58 +0530 Subject: [PATCH 01/15] fix: check confirmation for preconfirmed txns and add subsidy --- tools/preconf-rpc/handlers/handlers.go | 3 +- tools/preconf-rpc/sender/sender.go | 42 +++++++++++++++----------- tools/preconf-rpc/service/service.go | 29 ++++++++++++++++++ tools/preconf-rpc/store/store.go | 38 +++++++++++++++++++++-- 4 files changed, 90 insertions(+), 22 deletions(-) diff --git a/tools/preconf-rpc/handlers/handlers.go b/tools/preconf-rpc/handlers/handlers.go index df6023ccb..fe0240498 100644 --- a/tools/preconf-rpc/handlers/handlers.go +++ b/tools/preconf-rpc/handlers/handlers.go @@ -411,8 +411,7 @@ func (h *rpcMethodHandler) handleGetTxReceipt(ctx context.Context, params ...any return nil, true, nil } - if txn.Status != sender.TxStatusFailed && - (txn.Status != sender.TxStatusPreConfirmed || h.blockTracker.LatestBlockNumber() > uint64(txn.BlockNumber)) { + if txn.Status != sender.TxStatusFailed && txn.Status != sender.TxStatusPreConfirmed { return nil, true, nil } diff --git a/tools/preconf-rpc/sender/sender.go b/tools/preconf-rpc/sender/sender.go index c444dfcef..c6ca06217 100644 --- a/tools/preconf-rpc/sender/sender.go +++ b/tools/preconf-rpc/sender/sender.go @@ -422,6 +422,7 @@ BID_LOOP: default: } + preConfirmed := false result, err = t.sendBid(ctx, txn) switch { case err != nil: @@ -454,8 +455,10 @@ BID_LOOP: "blockNumber", result.blockNumber, "bidAmount", result.bidAmount.String(), ) - t.clearBlockAttemptHistory(txn) - break BID_LOOP + if err := t.store.StoreTransaction(ctx, txn, result.commitments); err != nil { + return fmt.Errorf("failed to store fast-tracked transaction: %w", err) + } + preConfirmed = true case result.optedInSlot: if result.noOfProviders == len(result.commitments) { // This means that all builders have committed to the bid and it @@ -470,13 +473,15 @@ BID_LOOP: "blockNumber", result.blockNumber, "bidAmount", result.bidAmount.String(), ) - t.clearBlockAttemptHistory(txn) - break BID_LOOP + if err := t.store.StoreTransaction(ctx, txn, result.commitments); err != nil { + return fmt.Errorf("failed to store preconfirmed transaction: %w", err) + } + preConfirmed = true } default: } - if result.noOfProviders > len(result.commitments) { + if !preConfirmed && result.noOfProviders > len(result.commitments) { t.logger.Warn( "Not all builders committed to the bid", "noOfProviders", result.noOfProviders, @@ -501,24 +506,25 @@ BID_LOOP: return fmt.Errorf("failed to check transaction inclusion: %w", err) } if included { - txn.Status = TxStatusConfirmed - txn.BlockNumber = int64(result.blockNumber) - t.logger.Info( - "Transaction confirmed for non opted-in slot", - "sender", txn.Sender.Hex(), - "type", txn.Type, - "blockNumber", result.blockNumber, - "bidAmount", result.bidAmount.String(), - ) + if !preConfirmed { + txn.Status = TxStatusConfirmed + txn.BlockNumber = int64(result.blockNumber) + t.logger.Info( + "Transaction confirmed for non opted-in slot", + "sender", txn.Sender.Hex(), + "type", txn.Type, + "blockNumber", result.blockNumber, + "bidAmount", result.bidAmount.String(), + ) + if err := t.store.StoreTransaction(ctx, txn, result.commitments); err != nil { + return fmt.Errorf("failed to store preconfirmed transaction: %w", err) + } + } t.clearBlockAttemptHistory(txn) break BID_LOOP } } - if err := t.store.StoreTransaction(ctx, txn, result.commitments); err != nil { - return fmt.Errorf("failed to store preconfirmed transaction: %w", err) - } - switch txn.Type { case TxTypeRegular: if err := t.store.DeductBalance(ctx, txn.Sender, result.bidAmount); err != nil { diff --git a/tools/preconf-rpc/service/service.go b/tools/preconf-rpc/service/service.go index cf24c77f6..fa4ba1540 100644 --- a/tools/preconf-rpc/service/service.go +++ b/tools/preconf-rpc/service/service.go @@ -404,6 +404,35 @@ func New(config *Config) (*Service, error) { } }) + mux.HandleFunc("POST /subsidize", func(w http.ResponseWriter, r *http.Request) { + if err := checkAuthorization(r); err != nil { + http.Error(w, err.Error(), http.StatusUnauthorized) + return + } + + // Get account address and amount from URL params + account := r.URL.Query().Get("account") + if account == "" || !common.IsHexAddress(account) { + http.Error(w, "invalid or missing account address", http.StatusBadRequest) + return + } + + amountStr := r.URL.Query().Get("amount") + amount, ok := new(big.Int).SetString(amountStr, 10) + if !ok { + http.Error(w, "invalid amount", http.StatusBadRequest) + return + } + + if err := rpcstore.AddSubsidy(r.Context(), common.HexToAddress(account), amount); err != nil { + http.Error(w, fmt.Sprintf("failed to add subsidy: %v", err), http.StatusInternalServerError) + return + } + + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte("OK")) + }) + srv := http.Server{ Addr: fmt.Sprintf(":%d", config.HTTPPort), Handler: mux, diff --git a/tools/preconf-rpc/store/store.go b/tools/preconf-rpc/store/store.go index 1fb34479d..97d4b5dd5 100644 --- a/tools/preconf-rpc/store/store.go +++ b/tools/preconf-rpc/store/store.go @@ -48,6 +48,12 @@ CREATE TABLE IF NOT EXISTS balances ( balance NUMERIC(24, 0) );` +var subsidiesTable = ` +CREATE TABLE IF NOT EXISTS subsidies ( + account TEXT PRIMARY KEY, + balance NUMERIC(24, 0) +);` + type rpcstore struct { db *sql.DB } @@ -57,6 +63,7 @@ func New(db *sql.DB) (*rpcstore, error) { transactionsTable, commitmentsTable, balancesTable, + subsidiesTable, } { _, err := db.Exec(table) if err != nil { @@ -279,7 +286,7 @@ func (s *rpcstore) StoreTransaction( insertCommitment := ` INSERT INTO commitments (commitment_digest, transaction_hash, provider_address, commitment_data) VALUES ($1, $2, $3, $4) - ON CONFLICT (commitment_digest) DO NOTHING; + ON CONFLICT (commitment_digest) DO UPDATE SET; ` commitmentData, err := proto.Marshal(commitment) if err != nil { @@ -353,7 +360,7 @@ func (s *rpcstore) GetCurrentNonce(ctx context.Context, sender common.Address) u query := ` SELECT COALESCE(MAX(nonce), 0) FROM mcTransactions - WHERE sender = $1 AND status = 'pending'; + WHERE sender = $1 AND (status = 'pending' OR status = 'pre-confirmed'); ` row := s.db.QueryRowContext(ctx, query, sender.Hex()) var nextNonce uint64 @@ -480,3 +487,30 @@ func (s *rpcstore) GetBalance( return balanceInt, nil } + +func (s *rpcstore) AddSubsidy( + ctx context.Context, + account common.Address, + amount *big.Int, +) error { + if account == (common.Address{}) || amount == nil || amount.Sign() <= 0 { + return fmt.Errorf("invalid account or amount: account=%s, amount=%s", account.Hex(), amount.String()) + } + + query := ` + INSERT INTO subsidies (account, balance) + VALUES ($1, $2) + ON CONFLICT (account) DO UPDATE SET balance = balances.balance + $2 + WHERE balances.balance + $2 >= 0; + ` + + _, err := s.db.ExecContext(ctx, query, account.Hex(), amount.String()) + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + return fmt.Errorf("account %s not found or insufficient balance: %w", account.Hex(), ErrInsufficientBalance) + } + return fmt.Errorf("failed to add balance for account %s: %w", account.Hex(), err) + } + + return s.AddBalance(ctx, account, amount) +} From c8a1f41ee8c730b35d96ef06d16dc50e2fa4e1f0 Mon Sep 17 00:00:00 2001 From: Alok Nerurkar Date: Sat, 18 Oct 2025 20:38:31 +0530 Subject: [PATCH 02/15] fix: check confirmation for preconfirmed txns and add subsidy --- tools/preconf-rpc/handlers/handlers.go | 4 +++- tools/preconf-rpc/sender/sender.go | 11 ++++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/tools/preconf-rpc/handlers/handlers.go b/tools/preconf-rpc/handlers/handlers.go index fe0240498..e2c47ba6f 100644 --- a/tools/preconf-rpc/handlers/handlers.go +++ b/tools/preconf-rpc/handlers/handlers.go @@ -43,6 +43,7 @@ type BlockTracker interface { type Sender interface { Enqueue(ctx context.Context, txn *sender.Transaction) error CancelTransaction(ctx context.Context, txHash common.Hash) (bool, error) + IsTransactionInFlight(txnHash common.Hash) bool } type rpcMethodHandler struct { @@ -411,7 +412,8 @@ func (h *rpcMethodHandler) handleGetTxReceipt(ctx context.Context, params ...any return nil, true, nil } - if txn.Status != sender.TxStatusFailed && txn.Status != sender.TxStatusPreConfirmed { + if txn.Status != sender.TxStatusFailed && + (txn.Status != sender.TxStatusPreConfirmed || !h.sndr.IsTransactionInFlight(txHash)) { return nil, true, nil } diff --git a/tools/preconf-rpc/sender/sender.go b/tools/preconf-rpc/sender/sender.go index c6ca06217..9c86863dc 100644 --- a/tools/preconf-rpc/sender/sender.go +++ b/tools/preconf-rpc/sender/sender.go @@ -234,6 +234,13 @@ func (t *TxSender) Enqueue(ctx context.Context, tx *Transaction) error { return nil } +func (t *TxSender) IsTransactionInflight(txnHash common.Hash) bool { + t.inflightMu.RLock() + _, found := t.inflightTxns[txnHash] + t.inflightMu.RUnlock() + return found +} + func (t *TxSender) CancelTransaction(ctx context.Context, txnHash common.Hash) (bool, error) { t.inflightMu.RLock() cancel, found := t.inflightTxns[txnHash] @@ -277,9 +284,7 @@ func (t *TxSender) CancelTransaction(ctx context.Context, txnHash common.Hash) ( t.logger.Info("Context cancelled while waiting for transaction cancellation") return false, ctx.Err() case <-ticker.C: - t.inflightMu.RLock() - _, stillInFlight := t.inflightTxns[txnHash] - t.inflightMu.RUnlock() + stillInFlight := t.IsTransactionInflight(txnHash) if !stillInFlight { txn, err := t.store.GetTransactionByHash(ctx, txnHash) switch { From ae0d6099c90ec52ebdb7a0fc8596761889298235 Mon Sep 17 00:00:00 2001 From: Harsh pratap Singh Date: Sun, 19 Oct 2025 11:09:45 +0530 Subject: [PATCH 03/15] fix: correct typo --- tools/preconf-rpc/sender/sender.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/preconf-rpc/sender/sender.go b/tools/preconf-rpc/sender/sender.go index 9c86863dc..02fcd69d1 100644 --- a/tools/preconf-rpc/sender/sender.go +++ b/tools/preconf-rpc/sender/sender.go @@ -234,7 +234,7 @@ func (t *TxSender) Enqueue(ctx context.Context, tx *Transaction) error { return nil } -func (t *TxSender) IsTransactionInflight(txnHash common.Hash) bool { +func (t *TxSender) IsTransactionInFlight(txnHash common.Hash) bool { t.inflightMu.RLock() _, found := t.inflightTxns[txnHash] t.inflightMu.RUnlock() @@ -284,7 +284,7 @@ func (t *TxSender) CancelTransaction(ctx context.Context, txnHash common.Hash) ( t.logger.Info("Context cancelled while waiting for transaction cancellation") return false, ctx.Err() case <-ticker.C: - stillInFlight := t.IsTransactionInflight(txnHash) + stillInFlight := t.IsTransactionInFlight(txnHash) if !stillInFlight { txn, err := t.store.GetTransactionByHash(ctx, txnHash) switch { From e41e82890c827b5a3c58c4bf00d0a3c33d8e8093 Mon Sep 17 00:00:00 2001 From: Alok Nerurkar Date: Sun, 19 Oct 2025 23:16:34 +0530 Subject: [PATCH 04/15] fix: check confirmation for preconfirmed txns and add subsidy --- tools/preconf-rpc/store/store.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/preconf-rpc/store/store.go b/tools/preconf-rpc/store/store.go index 97d4b5dd5..b33004589 100644 --- a/tools/preconf-rpc/store/store.go +++ b/tools/preconf-rpc/store/store.go @@ -500,8 +500,8 @@ func (s *rpcstore) AddSubsidy( query := ` INSERT INTO subsidies (account, balance) VALUES ($1, $2) - ON CONFLICT (account) DO UPDATE SET balance = balances.balance + $2 - WHERE balances.balance + $2 >= 0; + ON CONFLICT (account) DO UPDATE SET balance = subsidies.balance + $2 + WHERE subsidies.balance + $2 >= 0; ` _, err := s.db.ExecContext(ctx, query, account.Hex(), amount.String()) From dc68ada4127e0d00981454e05519c40e1b88f93d Mon Sep 17 00:00:00 2001 From: Alok Nerurkar Date: Mon, 20 Oct 2025 01:19:36 +0530 Subject: [PATCH 05/15] fix: check confirmation for preconfirmed txns and add subsidy --- tools/preconf-rpc/store/store.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/preconf-rpc/store/store.go b/tools/preconf-rpc/store/store.go index b33004589..f54d47f05 100644 --- a/tools/preconf-rpc/store/store.go +++ b/tools/preconf-rpc/store/store.go @@ -286,7 +286,7 @@ func (s *rpcstore) StoreTransaction( insertCommitment := ` INSERT INTO commitments (commitment_digest, transaction_hash, provider_address, commitment_data) VALUES ($1, $2, $3, $4) - ON CONFLICT (commitment_digest) DO UPDATE SET; + ON CONFLICT (commitment_digest) DO UPDATE SET commitment_data = EXCLUDED.commitment_data; ` commitmentData, err := proto.Marshal(commitment) if err != nil { From a56192abf7f7629e43b10b1acb2fc2e1437c4bc4 Mon Sep 17 00:00:00 2001 From: Alok Date: Mon, 20 Oct 2025 17:06:54 +0530 Subject: [PATCH 06/15] perf: add cache for proxied read requests --- tools/preconf-rpc/sender/sender.go | 33 +++++++++++++++++++------ tools/preconf-rpc/sender/sender_test.go | 12 ++++++++- tools/preconf-rpc/service/service.go | 17 +++++++++++++ 3 files changed, 53 insertions(+), 9 deletions(-) diff --git a/tools/preconf-rpc/sender/sender.go b/tools/preconf-rpc/sender/sender.go index 02fcd69d1..5b7d85516 100644 --- a/tools/preconf-rpc/sender/sender.go +++ b/tools/preconf-rpc/sender/sender.go @@ -37,12 +37,12 @@ const ( ) const ( - blockTime = 12 // seconds, typical Ethereum block time - bidTimeout = 100 * time.Millisecond // timeout for bid operations - defaultConfidence = 90 // default confidence level for the next block - confidenceSecondAttempt = 95 // confidence level for the second attempt - confidenceSubsequentAttempts = 99 // confidence level for subsequent attempts - transactionTimeout = 10 * time.Minute // timeout for transaction processing + blockTime = 12 // seconds, typical Ethereum block time + bidTimeout = 3 * time.Second // timeout for bid operation + defaultConfidence = 90 // default confidence level for the next block + confidenceSecondAttempt = 95 // confidence level for the second attempt + confidenceSubsequentAttempts = 99 // confidence level for subsequent attempts + transactionTimeout = 10 * time.Minute // timeout for transaction processing ) var ( @@ -135,6 +135,8 @@ type TxSender struct { txnAttemptHistory *lru.Cache[common.Hash, *txnAttempt] notifier Notifier fastTrack func(cmts []*bidderapiv1.Commitment, optedInSlot bool) bool + bidTimeout time.Duration + timeoutMtx sync.RWMutex } func noOpFastTrack(_ []*bidderapiv1.Commitment, _ bool) bool { @@ -177,6 +179,7 @@ func NewTxSender( txnAttemptHistory: txnAttemptHistory, notifier: notifier, fastTrack: fastTrack, + bidTimeout: bidTimeout, }, nil } @@ -312,6 +315,20 @@ func (t *TxSender) CancelTransaction(ctx context.Context, txnHash common.Hash) ( } } +func (t *TxSender) UpdateBidTimeout(timeout time.Duration) { + t.timeoutMtx.Lock() + defer t.timeoutMtx.Unlock() + + t.bidTimeout = timeout +} + +func (t *TxSender) getBidTimeout() time.Duration { + t.timeoutMtx.RLock() + defer t.timeoutMtx.RUnlock() + + return t.bidTimeout +} + func (t *TxSender) Start(ctx context.Context) chan struct{} { t.eg, t.egCtx = errgroup.WithContext(ctx) done := make(chan struct{}) @@ -608,7 +625,7 @@ func (t *TxSender) sendBid( // Allow for certain level of tolerance w.r.t timestamps optedInSlot := math.Abs(float64(timeToOptIn)-float64(timeUntilNextBlock.Seconds())) < float64(blockTime/3) - cctx, cancel := context.WithTimeout(ctx, bidTimeout) + cctx, cancel := context.WithTimeout(ctx, t.getBidTimeout()) defer cancel() cost, err := t.calculatePriceForNextBlock(txn, bidBlockNo, prices, optedInSlot) @@ -674,7 +691,7 @@ func (t *TxSender) sendBid( WaitForOptIn: false, BlockNumber: uint64(bidBlockNo), RevertingTxHashes: []string{txn.Hash().Hex()}, - DecayDuration: bidTimeout * 2, + DecayDuration: t.getBidTimeout() * 2, }, ) if err != nil { diff --git a/tools/preconf-rpc/sender/sender_test.go b/tools/preconf-rpc/sender/sender_test.go index e22263f8d..705174b2e 100644 --- a/tools/preconf-rpc/sender/sender_test.go +++ b/tools/preconf-rpc/sender/sender_test.go @@ -399,6 +399,16 @@ func TestSender(t *testing.T) { t.Fatalf("expected 1 commitment, got %d", len(res.commitments)) } + checkOp := <-blockTracker.in + if checkOp.hash != tx1.Hash() { + t.Fatalf("expected transaction hash %s, got %s", tx1.Hash().Hex(), checkOp.hash.Hex()) + } + if checkOp.block != 1 { + t.Fatalf("expected block number 1, got %d", checkOp.block) + } + // Simulate transaction inclusion + blockTracker.out <- true + tx2 := &sender.Transaction{ Transaction: types.NewTransaction( 2, @@ -493,7 +503,7 @@ func TestSender(t *testing.T) { close(resC) bidder.out <- resC - checkOp := <-blockTracker.in + checkOp = <-blockTracker.in if checkOp.hash != tx2.Hash() { t.Fatalf("expected transaction hash %s, got %s", tx2.Hash().Hex(), checkOp.hash.Hex()) } diff --git a/tools/preconf-rpc/service/service.go b/tools/preconf-rpc/service/service.go index fa4ba1540..fe6b8e205 100644 --- a/tools/preconf-rpc/service/service.go +++ b/tools/preconf-rpc/service/service.go @@ -433,6 +433,23 @@ func New(config *Config) (*Service, error) { _, _ = w.Write([]byte("OK")) }) + mux.HandleFunc("POST /update_bid_timeout", func(w http.ResponseWriter, r *http.Request) { + if err := checkAuthorization(r); err != nil { + http.Error(w, err.Error(), http.StatusUnauthorized) + return + } + + timeoutStr := r.URL.Query().Get("timeout") + timeout, err := time.ParseDuration(timeoutStr) + if err != nil { + http.Error(w, "invalid timeout", http.StatusBadRequest) + return + } + sndr.UpdateBidTimeout(timeout) + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte("OK")) + }) + srv := http.Server{ Addr: fmt.Sprintf(":%d", config.HTTPPort), Handler: mux, From 2ffcc5c052bbeab2b47426b1b06eabf20b74db96 Mon Sep 17 00:00:00 2001 From: Alok Nerurkar Date: Tue, 21 Oct 2025 02:16:41 +0530 Subject: [PATCH 07/15] fix: check confirmation for preconfirmed txns and add subsidy --- tools/preconf-rpc/handlers/handlers.go | 4 +-- tools/preconf-rpc/notifier/notifier.go | 4 +-- tools/preconf-rpc/rpcserver/rpcserver.go | 10 +++----- tools/preconf-rpc/sender/sender.go | 31 ++++++++++++------------ 4 files changed, 22 insertions(+), 27 deletions(-) diff --git a/tools/preconf-rpc/handlers/handlers.go b/tools/preconf-rpc/handlers/handlers.go index e2c47ba6f..fe0240498 100644 --- a/tools/preconf-rpc/handlers/handlers.go +++ b/tools/preconf-rpc/handlers/handlers.go @@ -43,7 +43,6 @@ type BlockTracker interface { type Sender interface { Enqueue(ctx context.Context, txn *sender.Transaction) error CancelTransaction(ctx context.Context, txHash common.Hash) (bool, error) - IsTransactionInFlight(txnHash common.Hash) bool } type rpcMethodHandler struct { @@ -412,8 +411,7 @@ func (h *rpcMethodHandler) handleGetTxReceipt(ctx context.Context, params ...any return nil, true, nil } - if txn.Status != sender.TxStatusFailed && - (txn.Status != sender.TxStatusPreConfirmed || !h.sndr.IsTransactionInFlight(txHash)) { + if txn.Status != sender.TxStatusFailed && txn.Status != sender.TxStatusPreConfirmed { return nil, true, nil } diff --git a/tools/preconf-rpc/notifier/notifier.go b/tools/preconf-rpc/notifier/notifier.go index 66ce1a6a6..185b134de 100644 --- a/tools/preconf-rpc/notifier/notifier.go +++ b/tools/preconf-rpc/notifier/notifier.go @@ -282,7 +282,7 @@ func (n *Notifier) StartTransactionNotifier( func (n *Notifier) NotifyTransactionStatus( txn *sender.Transaction, noOfAttempts int, - start time.Time, + timeTaken time.Duration, ) { n.queuedMu.Lock() defer n.queuedMu.Unlock() @@ -290,7 +290,7 @@ func (n *Notifier) NotifyTransactionStatus( n.queuedTxns = append(n.queuedTxns, txnInfo{ txn: txn, noOfAttempts: noOfAttempts, - timeTaken: time.Since(start).Round(time.Millisecond), + timeTaken: timeTaken, }) } diff --git a/tools/preconf-rpc/rpcserver/rpcserver.go b/tools/preconf-rpc/rpcserver/rpcserver.go index d4461cc2f..1a1257052 100644 --- a/tools/preconf-rpc/rpcserver/rpcserver.go +++ b/tools/preconf-rpc/rpcserver/rpcserver.go @@ -70,8 +70,6 @@ var cacheMethods = map[string]bool{ "eth_call": true, "eth_getCode": true, "eth_getStorageAt": true, - "eth_feeHistory": true, - "eth_gasPrice": true, "eth_getLogs": true, "net_version": true, } @@ -168,7 +166,7 @@ func (s *JSONRPCServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { start := time.Now() defer func() { - s.logger.Debug("Request processing time", "method", req.Method, "id", req.ID, "duration", time.Since(start)) + s.logger.Info("Request processing time", "method", req.Method, "id", req.ID, "duration", time.Since(start)) }() if cacheMethods[req.Method] { @@ -201,7 +199,7 @@ func (s *JSONRPCServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { } if cacheMethods[req.Method] && resp.Result != nil { key := cacheKey(req.Method, req.Params) - s.cache.Add(key, cacheEntry{ + _ = s.cache.Add(key, cacheEntry{ until: time.Now().Add(pickTTL(req.Method, *resp.Result)), data: *resp.Result, }) @@ -350,8 +348,6 @@ func pickTTL(method string, params json.RawMessage) time.Duration { return 24 * time.Hour case "eth_getCode": return 24 * time.Hour - case "eth_feeHistory": - return 3 * time.Second case "eth_call": // if block tag provided and hex number → immutable if strings.HasSuffix(string(params), "\"") { // cheap check @@ -359,7 +355,7 @@ func pickTTL(method string, params json.RawMessage) time.Duration { return 24 * time.Hour } } - return 1 * time.Second + return 1 * time.Millisecond default: return 2 * time.Second } diff --git a/tools/preconf-rpc/sender/sender.go b/tools/preconf-rpc/sender/sender.go index 5b7d85516..97f5b6a95 100644 --- a/tools/preconf-rpc/sender/sender.go +++ b/tools/preconf-rpc/sender/sender.go @@ -113,7 +113,7 @@ type txnAttempt struct { } type Notifier interface { - NotifyTransactionStatus(txn *Transaction, noOfAttempts int, start time.Time) + NotifyTransactionStatus(txn *Transaction, noOfAttempts int, timeTaken time.Duration) } type TxSender struct { @@ -237,13 +237,6 @@ func (t *TxSender) Enqueue(ctx context.Context, tx *Transaction) error { return nil } -func (t *TxSender) IsTransactionInFlight(txnHash common.Hash) bool { - t.inflightMu.RLock() - _, found := t.inflightTxns[txnHash] - t.inflightMu.RUnlock() - return found -} - func (t *TxSender) CancelTransaction(ctx context.Context, txnHash common.Hash) (bool, error) { t.inflightMu.RLock() cancel, found := t.inflightTxns[txnHash] @@ -287,7 +280,9 @@ func (t *TxSender) CancelTransaction(ctx context.Context, txnHash common.Hash) ( t.logger.Info("Context cancelled while waiting for transaction cancellation") return false, ctx.Err() case <-ticker.C: - stillInFlight := t.IsTransactionInFlight(txnHash) + t.inflightMu.RLock() + _, stillInFlight := t.inflightTxns[txnHash] + t.inflightMu.RUnlock() if !stillInFlight { txn, err := t.store.GetTransactionByHash(ctx, txnHash) switch { @@ -396,7 +391,7 @@ func (t *TxSender) processQueuedTransactions(ctx context.Context) { t.logger.Info("No queued transactions to process") return } - t.logger.Info("Processing queued transactions", "count", len(txns)) + t.logger.Debug("Processing queued transactions", "count", len(txns)) for _, txn := range txns { txn := txn // capture range variable select { @@ -420,7 +415,7 @@ func (t *TxSender) processQueuedTransactions(ctx context.Context) { t.logger.Error("Failed to process transaction", "sender", txn.Sender.Hex(), "error", err) txn.Status = TxStatusFailed txn.Details = err.Error() - t.clearBlockAttemptHistory(txn) + t.clearBlockAttemptHistory(txn, time.Now()) return t.store.StoreTransaction(ctx, txn, nil) } return nil @@ -472,6 +467,7 @@ BID_LOOP: txn.BlockNumber = int64(result.blockNumber) t.logger.Info( "Transaction fast-tracked based on commitments", + "transactionHash", txn.Hash().Hex(), "sender", txn.Sender.Hex(), "type", txn.Type, "blockNumber", result.blockNumber, @@ -490,6 +486,7 @@ BID_LOOP: txn.BlockNumber = int64(result.blockNumber) t.logger.Info( "Transaction pre-confirmed", + "transactionHash", txn.Hash().Hex(), "sender", txn.Sender.Hex(), "type", txn.Type, "blockNumber", result.blockNumber, @@ -506,6 +503,7 @@ BID_LOOP: if !preConfirmed && result.noOfProviders > len(result.commitments) { t.logger.Warn( "Not all builders committed to the bid", + "transactionHash", txn.Hash().Hex(), "noOfProviders", result.noOfProviders, "noOfCommitments", len(result.commitments), "sender", txn.Sender.Hex(), @@ -532,7 +530,8 @@ BID_LOOP: txn.Status = TxStatusConfirmed txn.BlockNumber = int64(result.blockNumber) t.logger.Info( - "Transaction confirmed for non opted-in slot", + "Transaction confirmed", + "transactionHash", txn.Hash().Hex(), "sender", txn.Sender.Hex(), "type", txn.Type, "blockNumber", result.blockNumber, @@ -542,7 +541,8 @@ BID_LOOP: return fmt.Errorf("failed to store preconfirmed transaction: %w", err) } } - t.clearBlockAttemptHistory(txn) + endTime := time.UnixMilli(result.commitments[len(result.commitments)-1].DispatchTimestamp) + t.clearBlockAttemptHistory(txn, endTime) break BID_LOOP } } @@ -810,7 +810,7 @@ func (t *TxSender) calculatePriceForNextBlock( ) } -func (t *TxSender) clearBlockAttemptHistory(txn *Transaction) { +func (t *TxSender) clearBlockAttemptHistory(txn *Transaction, endTime time.Time) { attempts, found := t.txnAttemptHistory.Get(txn.Hash()) if !found { return @@ -824,6 +824,7 @@ func (t *TxSender) clearBlockAttemptHistory(txn *Transaction) { t.logger.Info( "Clearing block attempt history for transaction", "hash", txn.Hash().Hex(), + "blockNumber", txn.BlockNumber, "blockAttempts", len(attempts.attempts), "startTime", attempts.startTime.Format(time.RFC3339), "startBlockNumber", attempts.attempts[0].blockNumber, @@ -832,5 +833,5 @@ func (t *TxSender) clearBlockAttemptHistory(txn *Transaction) { _ = t.txnAttemptHistory.Remove(txn.Hash()) - t.notifier.NotifyTransactionStatus(txn, totalAttempts, attempts.startTime) + t.notifier.NotifyTransactionStatus(txn, totalAttempts, endTime.Sub(attempts.startTime).Round(time.Millisecond)) } From c2cb91a13e0e82ac07760abe8532bc436fb2999f Mon Sep 17 00:00:00 2001 From: Alok Nerurkar Date: Tue, 21 Oct 2025 02:22:27 +0530 Subject: [PATCH 08/15] fix: check confirmation for preconfirmed txns and add subsidy --- tools/preconf-rpc/sender/sender_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/preconf-rpc/sender/sender_test.go b/tools/preconf-rpc/sender/sender_test.go index 705174b2e..57f92f11f 100644 --- a/tools/preconf-rpc/sender/sender_test.go +++ b/tools/preconf-rpc/sender/sender_test.go @@ -261,7 +261,7 @@ type mockNotifier struct { notifications []string } -func (m *mockNotifier) NotifyTransactionStatus(txn *sender.Transaction, attempts int, start time.Time) { +func (m *mockNotifier) NotifyTransactionStatus(txn *sender.Transaction, attempts int, start time.Duration) { m.notifications = append(m.notifications, txn.Hash().Hex()) } From ec9b5e8d72b09f8ce5f125c76eb4f9ffb9d08cbb Mon Sep 17 00:00:00 2001 From: Alok Nerurkar Date: Tue, 21 Oct 2025 02:30:02 +0530 Subject: [PATCH 09/15] fix: check confirmation for preconfirmed txns and add subsidy --- tools/preconf-rpc/handlers/handlers.go | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/tools/preconf-rpc/handlers/handlers.go b/tools/preconf-rpc/handlers/handlers.go index fe0240498..a2bfb8e55 100644 --- a/tools/preconf-rpc/handlers/handlers.go +++ b/tools/preconf-rpc/handlers/handlers.go @@ -570,11 +570,8 @@ func (h *rpcMethodHandler) handleMevCommitGetBalance(ctx context.Context, params balance, err := h.store.GetBalance(ctx, common.HexToAddress(account)) if err != nil { - h.logger.Error("Failed to get balance for account", "error", err, "account", account) - return nil, false, rpcserver.NewJSONErr( - rpcserver.CodeCustomError, - "failed to get balance for account", - ) + h.logger.Warn("Failed to get balance for account, returning 0", "error", err, "account", account) + balance = big.NewInt(0) } return json.RawMessage(fmt.Sprintf(`{"balance": "%s"}`, balance)), false, nil From 0fe1b668fc452c34423d981c231d550d0c385505 Mon Sep 17 00:00:00 2001 From: Alok Nerurkar Date: Tue, 21 Oct 2025 11:29:58 +0530 Subject: [PATCH 10/15] fix: check confirmation for preconfirmed txns and add subsidy --- tools/preconf-rpc/rpcserver/rpcserver.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/preconf-rpc/rpcserver/rpcserver.go b/tools/preconf-rpc/rpcserver/rpcserver.go index 1a1257052..1d4a20af8 100644 --- a/tools/preconf-rpc/rpcserver/rpcserver.go +++ b/tools/preconf-rpc/rpcserver/rpcserver.go @@ -166,7 +166,7 @@ func (s *JSONRPCServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { start := time.Now() defer func() { - s.logger.Info("Request processing time", "method", req.Method, "id", req.ID, "duration", time.Since(start)) + s.logger.Info("Request processing time", "method", req.Method, "id", req.ID, "duration", time.Since(start).String()) }() if cacheMethods[req.Method] { From 2d3722710ca70b8bbf885d1d57cefa622103e257 Mon Sep 17 00:00:00 2001 From: Alok Nerurkar Date: Tue, 21 Oct 2025 11:36:16 +0530 Subject: [PATCH 11/15] fix: check confirmation for preconfirmed txns and add subsidy --- tools/preconf-rpc/sender/sender.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tools/preconf-rpc/sender/sender.go b/tools/preconf-rpc/sender/sender.go index 97f5b6a95..a08d46b69 100644 --- a/tools/preconf-rpc/sender/sender.go +++ b/tools/preconf-rpc/sender/sender.go @@ -541,7 +541,10 @@ BID_LOOP: return fmt.Errorf("failed to store preconfirmed transaction: %w", err) } } - endTime := time.UnixMilli(result.commitments[len(result.commitments)-1].DispatchTimestamp) + endTime := time.Now() + if len(result.commitments) > 0 { + endTime = time.UnixMilli(result.commitments[len(result.commitments)-1].DispatchTimestamp) + } t.clearBlockAttemptHistory(txn, endTime) break BID_LOOP } From bd5bf284f7406151616d0d376546c3250fd64d4c Mon Sep 17 00:00:00 2001 From: Alok Nerurkar Date: Tue, 21 Oct 2025 11:47:30 +0530 Subject: [PATCH 12/15] fix: check confirmation for preconfirmed txns and add subsidy --- tools/preconf-rpc/handlers/handlers.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/preconf-rpc/handlers/handlers.go b/tools/preconf-rpc/handlers/handlers.go index a2bfb8e55..5d51d7436 100644 --- a/tools/preconf-rpc/handlers/handlers.go +++ b/tools/preconf-rpc/handlers/handlers.go @@ -83,7 +83,7 @@ func NewRPCMethodHandler( func (h *rpcMethodHandler) RegisterMethods(server *rpcserver.JSONRPCServer) { // Ethereum JSON-RPC methods overridden - server.RegisterHandler("eth_getBlockNumber", func(ctx context.Context, params ...any) (json.RawMessage, bool, error) { + server.RegisterHandler("eth_blockNumber", func(ctx context.Context, params ...any) (json.RawMessage, bool, error) { blockNumber := h.blockTracker.LatestBlockNumber() blockNumberJSON, err := json.Marshal(hexutil.Uint64(blockNumber)) From 08978bdb885f1d3e498a248bec35f46729237f52 Mon Sep 17 00:00:00 2001 From: Alok Nerurkar Date: Tue, 21 Oct 2025 13:26:15 +0530 Subject: [PATCH 13/15] fix: check confirmation for preconfirmed txns and add subsidy --- tools/preconf-rpc/blocktracker/blocktracker.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/preconf-rpc/blocktracker/blocktracker.go b/tools/preconf-rpc/blocktracker/blocktracker.go index 00caced10..ca82d4eab 100644 --- a/tools/preconf-rpc/blocktracker/blocktracker.go +++ b/tools/preconf-rpc/blocktracker/blocktracker.go @@ -90,6 +90,9 @@ func (b *blockTracker) NextBlockNumber() (uint64, time.Duration, error) { return 0, 0, errors.New("latest block not found in cache") } blockTime := time.Unix(int64(block.Time()), 0) + if time.Since(blockTime) >= 12*time.Second { + return b.latestBlockNo.Load() + 2, time.Until(blockTime.Add(24 * time.Second)), nil + } return b.latestBlockNo.Load() + 1, time.Until(blockTime.Add(12 * time.Second)), nil } From 4e2c6997b03451c1a6135d8cc07d86f7728ed51e Mon Sep 17 00:00:00 2001 From: Alok Nerurkar Date: Tue, 21 Oct 2025 15:13:11 +0530 Subject: [PATCH 14/15] fix: check confirmation for preconfirmed txns and add subsidy --- tools/preconf-rpc/blocktracker/blocktracker.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/preconf-rpc/blocktracker/blocktracker.go b/tools/preconf-rpc/blocktracker/blocktracker.go index ca82d4eab..b47e208df 100644 --- a/tools/preconf-rpc/blocktracker/blocktracker.go +++ b/tools/preconf-rpc/blocktracker/blocktracker.go @@ -90,7 +90,7 @@ func (b *blockTracker) NextBlockNumber() (uint64, time.Duration, error) { return 0, 0, errors.New("latest block not found in cache") } blockTime := time.Unix(int64(block.Time()), 0) - if time.Since(blockTime) >= 12*time.Second { + if time.Since(blockTime) >= 11*time.Second { return b.latestBlockNo.Load() + 2, time.Until(blockTime.Add(24 * time.Second)), nil } return b.latestBlockNo.Load() + 1, time.Until(blockTime.Add(12 * time.Second)), nil From 2464f62171f05354359f2f15e9e0e6454e7da0de Mon Sep 17 00:00:00 2001 From: Alok Nerurkar Date: Tue, 21 Oct 2025 15:15:00 +0530 Subject: [PATCH 15/15] fix: check confirmation for preconfirmed txns and add subsidy --- tools/preconf-rpc/blocktracker/blocktracker.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tools/preconf-rpc/blocktracker/blocktracker.go b/tools/preconf-rpc/blocktracker/blocktracker.go index b47e208df..3c6780efa 100644 --- a/tools/preconf-rpc/blocktracker/blocktracker.go +++ b/tools/preconf-rpc/blocktracker/blocktracker.go @@ -85,15 +85,16 @@ func (b *blockTracker) LatestBlockNumber() uint64 { } func (b *blockTracker) NextBlockNumber() (uint64, time.Duration, error) { - block, found := b.blocks.Get(b.latestBlockNo.Load()) + latestBlockNo := b.latestBlockNo.Load() + block, found := b.blocks.Get(latestBlockNo) if !found { return 0, 0, errors.New("latest block not found in cache") } blockTime := time.Unix(int64(block.Time()), 0) if time.Since(blockTime) >= 11*time.Second { - return b.latestBlockNo.Load() + 2, time.Until(blockTime.Add(24 * time.Second)), nil + return latestBlockNo + 2, time.Until(blockTime.Add(24 * time.Second)), nil } - return b.latestBlockNo.Load() + 1, time.Until(blockTime.Add(12 * time.Second)), nil + return latestBlockNo + 1, time.Until(blockTime.Add(12 * time.Second)), nil } func (b *blockTracker) CheckTxnInclusion(