Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 34 additions & 4 deletions token-price-oracle/updater/tx_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,27 +28,55 @@ func NewTxManager(l2Client *client.L2Client) *TxManager {

// SendTransaction sends a transaction in a thread-safe manner
// It ensures only one transaction is sent at a time to avoid nonce conflicts
// Before sending, it checks if there are any pending transactions by comparing nonces
func (m *TxManager) SendTransaction(ctx context.Context, txFunc func(*bind.TransactOpts) (*types.Transaction, error)) (*types.Receipt, error) {
m.mu.Lock()
defer m.mu.Unlock()

fromAddr := m.l2Client.WalletAddress()

// Check if there are pending transactions by comparing nonces
confirmedNonce, err := m.l2Client.GetClient().NonceAt(ctx, fromAddr, nil)
if err != nil {
return nil, fmt.Errorf("failed to get confirmed nonce: %w", err)
}

pendingNonce, err := m.l2Client.GetClient().PendingNonceAt(ctx, fromAddr)
if err != nil {
return nil, fmt.Errorf("failed to get pending nonce: %w", err)
}

if pendingNonce > confirmedNonce {
// There are pending transactions, don't send new one
log.Warn("Found pending transactions, skipping this round",
"address", fromAddr.Hex(),
"confirmed_nonce", confirmedNonce,
"pending_nonce", pendingNonce,
"pending_count", pendingNonce-confirmedNonce)
return nil, fmt.Errorf("pending transactions exist (confirmed: %d, pending: %d)", confirmedNonce, pendingNonce)
}

log.Info("No pending transactions, proceeding to send",
"address", fromAddr.Hex(),
"nonce", confirmedNonce)

// Get transaction options (returns a copy)
auth := m.l2Client.GetOpts()
auth.Context = ctx

// First, estimate gas with GasLimit = 0
auth.GasLimit = 0
auth.NoSend = true
tx, err := txFunc(auth)
if err != nil {
return nil, fmt.Errorf("failed to estimate gas: %w", err)
}

// Use 1.5x of estimated gas as the actual gas limit
estimatedGas := tx.Gas()
auth.GasLimit = estimatedGas * 3 / 2
log.Info("Gas estimation completed", "estimated", estimatedGas, "actual_limit", auth.GasLimit)

// Now send the actual transaction
auth.NoSend = false
tx, err = txFunc(auth)
Expand All @@ -58,16 +86,18 @@ func (m *TxManager) SendTransaction(ctx context.Context, txFunc func(*bind.Trans

log.Info("Transaction sent",
"tx_hash", tx.Hash().Hex(),
"nonce", tx.Nonce(),
"gas_limit", tx.Gas())

// Wait for transaction to be mined with custom timeout and retry logic
// Wait for transaction to be mined with timeout and retry logic
receipt, err := m.waitForReceipt(ctx, tx.Hash(), 60*time.Second, 2*time.Second)
if err != nil {
log.Error("Failed to wait for transaction receipt",
"tx_hash", tx.Hash().Hex(),
"error", err)
return nil, err
}

return receipt, nil
}

Expand Down