From 11eb1bd75e7c5678b74b57159d29de528accc200 Mon Sep 17 00:00:00 2001 From: Tanut Lertwarachai Date: Thu, 20 Nov 2025 16:12:39 +0700 Subject: [PATCH 1/9] add db alert --- relayer/alert/alert.go | 1 + relayer/chains/evm/provider.go | 66 ++++++++++++++++++++++++---------- relayer/db/types.go | 4 +-- 3 files changed, 50 insertions(+), 21 deletions(-) diff --git a/relayer/alert/alert.go b/relayer/alert/alert.go index 4bfc414..64e9622 100644 --- a/relayer/alert/alert.go +++ b/relayer/alert/alert.go @@ -11,6 +11,7 @@ const ( GetTunnelPacketErrorMsg = "Failed to get tunnel packet from BandChain" GetContractTunnelInfoErrorMsg = "Failed to get tunnel info from contract" PacketSigningStatusErrorMsg = "Failed tunnel packet signing status" + SaveDatabaseErrorMsg = "Failed to save to database" ) // Alert represents an object that triggers and resets alerts. diff --git a/relayer/chains/evm/provider.go b/relayer/chains/evm/provider.go index d902289..ff6fd70 100644 --- a/relayer/chains/evm/provider.go +++ b/relayer/chains/evm/provider.go @@ -171,7 +171,10 @@ func (cp *EVMChainProvider) RelayPacket(ctx context.Context, packet *bandtypes.P ) return fmt.Errorf("[EVMProvider] failed to estimate gas fee: %w", err) } - alert.HandleReset(cp.Alert, alert.NewTopic(alert.EstimateGasFeeErrorMsg).WithTunnelID(packet.TunnelID).WithChainName(cp.ChainName)) + alert.HandleReset( + cp.Alert, + alert.NewTopic(alert.EstimateGasFeeErrorMsg).WithTunnelID(packet.TunnelID).WithChainName(cp.ChainName), + ) var lastErr error var bumpGasErr error @@ -215,12 +218,38 @@ func (cp *EVMChainProvider) RelayPacket(ctx context.Context, packet *bandtypes.P if err := cp.saveUnconfirmedTransaction(txHash, types.TX_STATUS_PENDING, packet, freeSigner.GetAddress()); err != nil { log.Error("SaveTransaction error", "retry_count", retryCount, err) + cp.Alert.Trigger( + alert.NewTopic(alert.SaveDatabaseErrorMsg). + WithTunnelID(packet.TunnelID). + WithChainName(cp.ChainName). + GetFullTopic(), + err.Error(), + ) + } else { + cp.Alert.Reset(alert.NewTopic(alert.SaveDatabaseErrorMsg). + WithTunnelID(packet.TunnelID). + WithChainName(cp.ChainName). + GetFullTopic()) } txResult := cp.WaitForConfirmedTx(ctx, txHash, log) cp.handleMetrics(packet.TunnelID, createdAt, txResult) - cp.handleSaveTransaction(ctx, freeSigner.GetAddress(), balance, packet, txResult, retryCount, log) + if err := cp.handleSaveTransaction(ctx, freeSigner.GetAddress(), balance, packet, txResult, log); err != nil { + log.Error("SaveTransaction error", "retry_count", retryCount, err) + cp.Alert.Trigger( + alert.NewTopic(alert.SaveDatabaseErrorMsg). + WithTunnelID(packet.TunnelID). + WithChainName(cp.ChainName). + GetFullTopic(), + err.Error(), + ) + } else { + cp.Alert.Reset(alert.NewTopic(alert.SaveDatabaseErrorMsg). + WithTunnelID(packet.TunnelID). + WithChainName(cp.ChainName). + GetFullTopic()) + } if txResult.Status == types.TX_STATUS_SUCCESS { log.Info( @@ -376,19 +405,16 @@ func (cp *EVMChainProvider) handleSaveTransaction(ctx context.Context, oldBalance *big.Int, packet *bandtypes.Packet, txResult TxResult, - retryCount int, log logger.Logger, -) { +) error { + var err error switch txResult.Status { case types.TX_STATUS_SUCCESS, types.TX_STATUS_FAILED: - if err := cp.saveConfirmedTransaction(ctx, signerAddress, oldBalance, packet, txResult); err != nil { - log.Error("SaveTransaction error", "retry_count", retryCount, err) - } + err = cp.saveConfirmedTransaction(ctx, signerAddress, oldBalance, packet, txResult, log) default: - if err := cp.saveUnconfirmedTransaction(txResult.TxHash, txResult.Status, packet, signerAddress); err != nil { - log.Error("SaveTransaction error", "retry_count", retryCount, err) - } + err = cp.saveUnconfirmedTransaction(txResult.TxHash, txResult.Status, packet, signerAddress) } + return err } // CheckConfirmedTx checks the confirmed transaction status. @@ -807,6 +833,7 @@ func (cp *EVMChainProvider) saveConfirmedTransaction( oldBalance *big.Int, packet *bandtypes.Packet, txResult TxResult, + log logger.Logger, ) error { // db was disabled if cp.DB == nil { @@ -818,25 +845,26 @@ func (cp *EVMChainProvider) saveConfirmedTransaction( signalPrices = append(signalPrices, *db.NewSignalPrice(p.SignalID, p.Price)) } - var blockTimestamp time.Time - balanceDelta := decimal.NullDecimal{} - + var blockTimestamp *time.Time block, err := cp.Client.GetBlock(ctx, txResult.BlockNumber) if err != nil { - return fmt.Errorf("failed to get block: %w", err) + log.Error("failed to get block info for ", "block_number", txResult.BlockNumber, err) + } else { + timestamp := time.Unix(int64(block.Time()), 0).UTC() + blockTimestamp = ×tamp } - blockTimestamp = time.Unix(int64(block.Time()), 0).UTC() - + balanceDelta := decimal.NullDecimal{} // Compute new balance // Note: this may be incorrect if other transactions affected the user's balance during this period. if oldBalance != nil { newBalance, err := cp.Client.GetBalance(ctx, gethcommon.HexToAddress(signerAddress), txResult.BlockNumber) if err != nil { - return fmt.Errorf("failed to get balance: %w", err) + log.Error("failed to get new balance after transaction", "block_number", txResult.BlockNumber, err) + } else { + diff := new(big.Int).Sub(newBalance, oldBalance) + balanceDelta = decimal.NewNullDecimal(decimal.NewFromBigInt(diff, 0)) } - diff := new(big.Int).Sub(newBalance, oldBalance) - balanceDelta = decimal.NewNullDecimal(decimal.NewFromBigInt(diff, 0)) } tx := db.NewConfirmedTransaction( diff --git a/relayer/db/types.go b/relayer/db/types.go index bc03d94..6506bf5 100644 --- a/relayer/db/types.go +++ b/relayer/db/types.go @@ -23,7 +23,7 @@ type Transaction struct { BalanceDelta decimal.NullDecimal `gorm:"type:decimal"` SignalPrices []SignalPrice - BlockTimestamp time.Time `gorm:"default:NULL"` + BlockTimestamp *time.Time `gorm:"default:NULL"` CreatedAt time.Time UpdatedAt time.Time } @@ -64,7 +64,7 @@ func NewConfirmedTransaction( effectiveGasPrice decimal.NullDecimal, balanceDelta decimal.NullDecimal, signalPrices []SignalPrice, - blockTimestamp time.Time, + blockTimestamp *time.Time, ) *Transaction { return &Transaction{ TxHash: txHash, From 8013300b8b0176bf5e9e2acc2787e9c52ef01d08 Mon Sep 17 00:00:00 2001 From: Tanut Lertwarachai Date: Thu, 20 Nov 2025 16:33:28 +0700 Subject: [PATCH 2/9] error --- relayer/chains/evm/provider.go | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/relayer/chains/evm/provider.go b/relayer/chains/evm/provider.go index ff6fd70..6b488dc 100644 --- a/relayer/chains/evm/provider.go +++ b/relayer/chains/evm/provider.go @@ -2,6 +2,7 @@ package evm import ( "context" + "errors" "fmt" "math/big" "strings" @@ -235,7 +236,7 @@ func (cp *EVMChainProvider) RelayPacket(ctx context.Context, packet *bandtypes.P txResult := cp.WaitForConfirmedTx(ctx, txHash, log) cp.handleMetrics(packet.TunnelID, createdAt, txResult) - if err := cp.handleSaveTransaction(ctx, freeSigner.GetAddress(), balance, packet, txResult, log); err != nil { + if err := cp.handleSaveTransaction(ctx, freeSigner.GetAddress(), balance, packet, txResult); err != nil { log.Error("SaveTransaction error", "retry_count", retryCount, err) cp.Alert.Trigger( alert.NewTopic(alert.SaveDatabaseErrorMsg). @@ -405,12 +406,11 @@ func (cp *EVMChainProvider) handleSaveTransaction(ctx context.Context, oldBalance *big.Int, packet *bandtypes.Packet, txResult TxResult, - log logger.Logger, ) error { var err error switch txResult.Status { case types.TX_STATUS_SUCCESS, types.TX_STATUS_FAILED: - err = cp.saveConfirmedTransaction(ctx, signerAddress, oldBalance, packet, txResult, log) + err = cp.saveConfirmedTransaction(ctx, signerAddress, oldBalance, packet, txResult) default: err = cp.saveUnconfirmedTransaction(txResult.TxHash, txResult.Status, packet, signerAddress) } @@ -833,13 +833,14 @@ func (cp *EVMChainProvider) saveConfirmedTransaction( oldBalance *big.Int, packet *bandtypes.Packet, txResult TxResult, - log logger.Logger, ) error { // db was disabled if cp.DB == nil { return nil } + var errs []error + var signalPrices []db.SignalPrice for _, p := range packet.SignalPrices { signalPrices = append(signalPrices, *db.NewSignalPrice(p.SignalID, p.Price)) @@ -848,7 +849,7 @@ func (cp *EVMChainProvider) saveConfirmedTransaction( var blockTimestamp *time.Time block, err := cp.Client.GetBlock(ctx, txResult.BlockNumber) if err != nil { - log.Error("failed to get block info for ", "block_number", txResult.BlockNumber, err) + errs = append(errs, fmt.Errorf("get block %d: %w", txResult.BlockNumber, err)) } else { timestamp := time.Unix(int64(block.Time()), 0).UTC() blockTimestamp = ×tamp @@ -860,7 +861,7 @@ func (cp *EVMChainProvider) saveConfirmedTransaction( if oldBalance != nil { newBalance, err := cp.Client.GetBalance(ctx, gethcommon.HexToAddress(signerAddress), txResult.BlockNumber) if err != nil { - log.Error("failed to get new balance after transaction", "block_number", txResult.BlockNumber, err) + errs = append(errs, fmt.Errorf("get balance at block %d: %w", txResult.BlockNumber, err)) } else { diff := new(big.Int).Sub(newBalance, oldBalance) balanceDelta = decimal.NewNullDecimal(decimal.NewFromBigInt(diff, 0)) @@ -883,7 +884,11 @@ func (cp *EVMChainProvider) saveConfirmedTransaction( ) if err := cp.DB.AddOrUpdateTransaction(tx); err != nil { - return fmt.Errorf("failed to save transaction to database: %w", err) + errs = append(errs, fmt.Errorf("failed to save transaction to database: %w", err)) + } + + if len(errs) > 0 { + return errors.Join(errs...) } return nil From d9d63117585f485a291bc0dbe8af896daf5a1ab7 Mon Sep 17 00:00:00 2001 From: Tanut Lertwarachai Date: Fri, 21 Nov 2025 14:14:25 +0700 Subject: [PATCH 3/9] refac logic --- relayer/alert/alert.go | 2 + relayer/chains/evm/provider.go | 305 ++++++++++---------- relayer/chains/evm/provider_eip1559_test.go | 6 - relayer/chains/evm/provider_legacy_test.go | 2 - 4 files changed, 162 insertions(+), 153 deletions(-) diff --git a/relayer/alert/alert.go b/relayer/alert/alert.go index 64e9622..0944dc7 100644 --- a/relayer/alert/alert.go +++ b/relayer/alert/alert.go @@ -11,6 +11,8 @@ const ( GetTunnelPacketErrorMsg = "Failed to get tunnel packet from BandChain" GetContractTunnelInfoErrorMsg = "Failed to get tunnel info from contract" PacketSigningStatusErrorMsg = "Failed tunnel packet signing status" + GetBlockErrorMsg = "Failed to get block from chain" + GetBalanceErrorMsg = "Failed to prepare database transaction" SaveDatabaseErrorMsg = "Failed to save to database" ) diff --git a/relayer/chains/evm/provider.go b/relayer/chains/evm/provider.go index 6b488dc..0bdddd4 100644 --- a/relayer/chains/evm/provider.go +++ b/relayer/chains/evm/provider.go @@ -2,7 +2,6 @@ package evm import ( "context" - "errors" "fmt" "math/big" "strings" @@ -190,9 +189,21 @@ func (cp *EVMChainProvider) RelayPacket(ctx context.Context, packet *bandtypes.P continue } - balance, err := cp.Client.GetBalance(ctx, gethcommon.HexToAddress(freeSigner.GetAddress()), nil) - if err != nil { - log.Error("GetBalance error", err) + var balance *big.Int + if cp.DB != nil { + balance, err = cp.Client.GetBalance(ctx, gethcommon.HexToAddress(freeSigner.GetAddress()), nil) + if err != nil { + log.Error("Failed to get balance", "retry_count", retryCount, err) + cp.Alert.Trigger( + alert.NewTopic(alert.GetBalanceErrorMsg). + WithTunnelID(packet.TunnelID). + WithChainName(cp.ChainName). + GetFullTopic(), + err.Error(), + ) + } else { + cp.Alert.Reset(alert.NewTopic(alert.GetBalanceErrorMsg).GetFullTopic()) + } } // submit the transaction, if failed, bump gas and retry @@ -217,39 +228,36 @@ func (cp *EVMChainProvider) RelayPacket(ctx context.Context, packet *bandtypes.P "retry_count", retryCount, ) - if err := cp.saveUnconfirmedTransaction(txHash, types.TX_STATUS_PENDING, packet, freeSigner.GetAddress()); err != nil { - log.Error("SaveTransaction error", "retry_count", retryCount, err) - cp.Alert.Trigger( - alert.NewTopic(alert.SaveDatabaseErrorMsg). - WithTunnelID(packet.TunnelID). - WithChainName(cp.ChainName). - GetFullTopic(), - err.Error(), - ) - } else { - cp.Alert.Reset(alert.NewTopic(alert.SaveDatabaseErrorMsg). - WithTunnelID(packet.TunnelID). - WithChainName(cp.ChainName). - GetFullTopic()) + // save pending tx in db + if cp.DB != nil { + tx := cp.prepareUnconfirmedTransaction(txHash, types.TX_STATUS_PENDING, packet, freeSigner.GetAddress()) + cp.handleSaveTransaction(tx, log, retryCount) } txResult := cp.WaitForConfirmedTx(ctx, txHash, log) cp.handleMetrics(packet.TunnelID, createdAt, txResult) - if err := cp.handleSaveTransaction(ctx, freeSigner.GetAddress(), balance, packet, txResult); err != nil { - log.Error("SaveTransaction error", "retry_count", retryCount, err) - cp.Alert.Trigger( - alert.NewTopic(alert.SaveDatabaseErrorMsg). - WithTunnelID(packet.TunnelID). - WithChainName(cp.ChainName). - GetFullTopic(), - err.Error(), - ) - } else { - cp.Alert.Reset(alert.NewTopic(alert.SaveDatabaseErrorMsg). - WithTunnelID(packet.TunnelID). - WithChainName(cp.ChainName). - GetFullTopic()) + if cp.DB != nil { + var tx *db.Transaction + switch txResult.Status { + case types.TX_STATUS_SUCCESS, types.TX_STATUS_FAILED: + tx = cp.prepareConfirmedTransaction( + freeSigner.GetAddress(), + packet, + txResult, + balance, + log, + retryCount, + ) + default: + tx = cp.prepareUnconfirmedTransaction( + txHash, + txResult.Status, + packet, + freeSigner.GetAddress(), + ) + } + cp.handleSaveTransaction(tx, log, retryCount) } if txResult.Status == types.TX_STATUS_SUCCESS { @@ -295,6 +303,24 @@ func (cp *EVMChainProvider) RelayPacket(ctx context.Context, packet *bandtypes.P return fmt.Errorf("[EVMProvider] failed to relay packet after %d retries", cp.Config.MaxRetry) } +func (cp *EVMChainProvider) handleSaveTransaction(tx *db.Transaction, log logger.Logger, retryCount int) { + if err := cp.DB.AddOrUpdateTransaction(tx); err != nil { + log.Error("Save transaction error", "retry_count", retryCount, err) + cp.Alert.Trigger( + alert.NewTopic(alert.SaveDatabaseErrorMsg). + WithTunnelID(tx.TunnelID). + WithChainName(cp.ChainName). + GetFullTopic(), + err.Error(), + ) + } else { + cp.Alert.Reset(alert.NewTopic(alert.SaveDatabaseErrorMsg). + WithTunnelID(tx.TunnelID). + WithChainName(cp.ChainName). + GetFullTopic()) + } +} + // createAndSignRelayTx creates and signs the relay transaction. func (cp *EVMChainProvider) createAndSignRelayTx( ctx context.Context, @@ -400,21 +426,112 @@ func (cp *EVMChainProvider) handleMetrics(tunnelID uint64, createdAt time.Time, } } -// handleSaveTransaction saves the transaction to the database based on its status. -func (cp *EVMChainProvider) handleSaveTransaction(ctx context.Context, +func (cp *EVMChainProvider) prepareConfirmedTransaction( signerAddress string, - oldBalance *big.Int, packet *bandtypes.Packet, txResult TxResult, -) error { - var err error - switch txResult.Status { - case types.TX_STATUS_SUCCESS, types.TX_STATUS_FAILED: - err = cp.saveConfirmedTransaction(ctx, signerAddress, oldBalance, packet, txResult) - default: - err = cp.saveUnconfirmedTransaction(txResult.TxHash, txResult.Status, packet, signerAddress) + oldBalance *big.Int, + log logger.Logger, + retryCount int, +) *db.Transaction { + if cp.DB == nil { + return nil + } + + var signalPrices []db.SignalPrice + + for _, p := range packet.SignalPrices { + signalPrices = append(signalPrices, *db.NewSignalPrice(p.SignalID, p.Price)) + } + + var blockTimestamp *time.Time + block, err := cp.Client.GetBlock(context.Background(), txResult.BlockNumber) + if err != nil { + log.Error("Failed to get block", "retry_count", retryCount, err) + cp.Alert.Trigger( + alert.NewTopic(alert.GetBlockErrorMsg). + WithTunnelID(packet.TunnelID). + WithChainName(cp.ChainName). + GetFullTopic(), + err.Error(), + ) + } else { + timestamp := time.Unix(int64(block.Time()), 0).UTC() + blockTimestamp = ×tamp + cp.Alert.Reset(alert.NewTopic(alert.GetBlockErrorMsg).GetFullTopic()) + } + + balanceDelta := decimal.NullDecimal{} + // Compute new balance + // Note: this may be incorrect if other transactions affected the user's balance during this period. + if oldBalance != nil { + newBalance, err := cp.Client.GetBalance( + context.Background(), + gethcommon.HexToAddress(signerAddress), + txResult.BlockNumber, + ) + if err != nil { + log.Error("Failed to get balance", "retry_count", retryCount, err) + cp.Alert.Trigger( + alert.NewTopic(alert.GetBalanceErrorMsg). + WithTunnelID(packet.TunnelID). + WithChainName(cp.ChainName). + GetFullTopic(), + err.Error(), + ) + } else { + diff := new(big.Int).Sub(newBalance, oldBalance) + balanceDelta = decimal.NewNullDecimal(decimal.NewFromBigInt(diff, 0)) + cp.Alert.Reset(alert.NewTopic(alert.GetBalanceErrorMsg).GetFullTopic()) + } + } + + tx := db.NewConfirmedTransaction( + txResult.TxHash, + packet.TunnelID, + packet.Sequence, + cp.ChainName, + types.ChainTypeEVM, + signerAddress, + txResult.Status, + txResult.GasUsed, + txResult.EffectiveGasPrice, + balanceDelta, + signalPrices, + blockTimestamp, + ) + + return tx +} + +func (cp *EVMChainProvider) prepareUnconfirmedTransaction( + txHash string, + status types.TxStatus, + packet *bandtypes.Packet, + signerAddress string, +) *db.Transaction { + // db was disabled + if cp.DB == nil { + return nil + } + + var signalPrices []db.SignalPrice + for _, p := range packet.SignalPrices { + signalPrices = append(signalPrices, *db.NewSignalPrice(p.SignalID, p.Price)) } - return err + + tx := db.NewUnconfirmedTransaction( + txHash, + packet.TunnelID, + packet.Sequence, + cp.ChainName, + types.ChainTypeEVM, + signerAddress, + status, + signalPrices, + ) + + return tx } // CheckConfirmedTx checks the confirmed transaction status. @@ -791,105 +908,3 @@ func (cp *EVMChainProvider) queryRelayerGasFee(ctx context.Context) (*big.Int, e return output, nil } - -func (cp *EVMChainProvider) saveUnconfirmedTransaction( - txHash string, - txStatus types.TxStatus, - packet *bandtypes.Packet, - sender string, -) error { - // db was disabled - if cp.DB == nil { - return nil - } - - var signalPrices []db.SignalPrice - for _, p := range packet.SignalPrices { - signalPrices = append(signalPrices, *db.NewSignalPrice(p.SignalID, p.Price)) - } - - tx := db.NewUnconfirmedTransaction( - txHash, - packet.TunnelID, - packet.Sequence, - cp.ChainName, - types.ChainTypeEVM, - sender, - txStatus, - signalPrices, - ) - - if err := cp.DB.AddOrUpdateTransaction(tx); err != nil { - return fmt.Errorf("failed to save transaction to database: %w", err) - } - - return nil -} - -// saveConfirmedTransaction stores the transaction result and related metadata (e.g. gas, status, balance delta) to the database if enabled. -func (cp *EVMChainProvider) saveConfirmedTransaction( - ctx context.Context, - signerAddress string, - oldBalance *big.Int, - packet *bandtypes.Packet, - txResult TxResult, -) error { - // db was disabled - if cp.DB == nil { - return nil - } - - var errs []error - - var signalPrices []db.SignalPrice - for _, p := range packet.SignalPrices { - signalPrices = append(signalPrices, *db.NewSignalPrice(p.SignalID, p.Price)) - } - - var blockTimestamp *time.Time - block, err := cp.Client.GetBlock(ctx, txResult.BlockNumber) - if err != nil { - errs = append(errs, fmt.Errorf("get block %d: %w", txResult.BlockNumber, err)) - } else { - timestamp := time.Unix(int64(block.Time()), 0).UTC() - blockTimestamp = ×tamp - } - - balanceDelta := decimal.NullDecimal{} - // Compute new balance - // Note: this may be incorrect if other transactions affected the user's balance during this period. - if oldBalance != nil { - newBalance, err := cp.Client.GetBalance(ctx, gethcommon.HexToAddress(signerAddress), txResult.BlockNumber) - if err != nil { - errs = append(errs, fmt.Errorf("get balance at block %d: %w", txResult.BlockNumber, err)) - } else { - diff := new(big.Int).Sub(newBalance, oldBalance) - balanceDelta = decimal.NewNullDecimal(decimal.NewFromBigInt(diff, 0)) - } - } - - tx := db.NewConfirmedTransaction( - txResult.TxHash, - packet.TunnelID, - packet.Sequence, - cp.ChainName, - types.ChainTypeEVM, - signerAddress, - txResult.Status, - txResult.GasUsed, - txResult.EffectiveGasPrice, - balanceDelta, - signalPrices, - blockTimestamp, - ) - - if err := cp.DB.AddOrUpdateTransaction(tx); err != nil { - errs = append(errs, fmt.Errorf("failed to save transaction to database: %w", err)) - } - - if len(errs) > 0 { - return errors.Join(errs...) - } - - return nil -} diff --git a/relayer/chains/evm/provider_eip1559_test.go b/relayer/chains/evm/provider_eip1559_test.go index 21e4b5b..20c2ee2 100644 --- a/relayer/chains/evm/provider_eip1559_test.go +++ b/relayer/chains/evm/provider_eip1559_test.go @@ -107,7 +107,6 @@ func (s *EIP1559ProviderTestSuite) TestRelayPacketSuccess() { GasTipCap: s.gasInfo.GasPriorityFee, }).Return(uint64(200_000), nil) - s.client.EXPECT().GetBalance(gomock.Any(), s.mockSignerAddress, nil).Return(big.NewInt(10000), nil) txHash := "0xabc123" s.client.EXPECT().BroadcastTx(gomock.Any(), gomock.Any()).Return(txHash, nil) s.client.EXPECT().GetTxReceipt(gomock.Any(), txHash).Return(&evm.TxReceipt{ @@ -137,7 +136,6 @@ func (s *EIP1559ProviderTestSuite) TestRelayPacketSuccessWithoutQueryMaxGasFee() GasTipCap: big.NewInt(3_000_000_000), }).Return(uint64(200_000), nil) - s.client.EXPECT().GetBalance(gomock.Any(), s.mockSignerAddress, nil).Return(big.NewInt(10000), nil) txHash := "0xabc123" s.client.EXPECT().BroadcastTx(gomock.Any(), gomock.Any()).Return(txHash, nil) s.client.EXPECT().GetTxReceipt(gomock.Any(), txHash).Return(&evm.TxReceipt{ @@ -182,8 +180,6 @@ func (s *EIP1559ProviderTestSuite) TestRelayPacketFailedBroadcastTx() { GasTipCap: s.gasInfo.GasPriorityFee, }).Return(uint64(200_000), nil).Times(s.chainProvider.Config.MaxRetry) - s.client.EXPECT().GetBalance(gomock.Any(), s.mockSignerAddress, nil).Return(big.NewInt(10000), nil). - Times(s.chainProvider.Config.MaxRetry) s.client.EXPECT(). BroadcastTx(gomock.Any(), gomock.Any()). Return("", fmt.Errorf("failed to broadcast an evm transaction")). @@ -204,8 +200,6 @@ func (s *EIP1559ProviderTestSuite) TestRelayPacketFailedTxReceiptStatus() { GasTipCap: s.gasInfo.GasPriorityFee, }).Return(uint64(200_000), nil).Times(s.chainProvider.Config.MaxRetry) - s.client.EXPECT().GetBalance(gomock.Any(), s.mockSignerAddress, nil).Return(big.NewInt(10000), nil). - Times(s.chainProvider.Config.MaxRetry) txHash := "0xabc123" s.client.EXPECT(). BroadcastTx(gomock.Any(), gomock.Any()). diff --git a/relayer/chains/evm/provider_legacy_test.go b/relayer/chains/evm/provider_legacy_test.go index eb5817f..ac4fac7 100644 --- a/relayer/chains/evm/provider_legacy_test.go +++ b/relayer/chains/evm/provider_legacy_test.go @@ -105,7 +105,6 @@ func (s *LegacyProviderTestSuite) TestRelayPacketSuccess() { GasPrice: s.gasInfo.GasPrice, }).Return(uint64(200_000), nil).AnyTimes() - s.client.EXPECT().GetBalance(gomock.Any(), s.mockSignerAddress, nil).Return(big.NewInt(10000), nil) txHash := "0xabc123" s.client.EXPECT().BroadcastTx(gomock.Any(), gomock.Any()).Return(txHash, nil) s.client.EXPECT().GetTxReceipt(gomock.Any(), txHash).Return(&evm.TxReceipt{ @@ -133,7 +132,6 @@ func (s *LegacyProviderTestSuite) TestRelayPacketSuccessWithoutQueryMaxGasFee() GasPrice: big.NewInt(2_000_000_000), }).Return(uint64(200_000), nil) - s.client.EXPECT().GetBalance(gomock.Any(), s.mockSignerAddress, nil).Return(big.NewInt(10000), nil) txHash := "0xabc123" s.client.EXPECT().BroadcastTx(gomock.Any(), gomock.Any()).Return(txHash, nil) s.client.EXPECT().GetTxReceipt(gomock.Any(), txHash).Return(&evm.TxReceipt{ From c16a9700162fdb7d2eb130ae1aba0174356a8c5d Mon Sep 17 00:00:00 2001 From: Tanut Lertwarachai Date: Fri, 21 Nov 2025 14:19:51 +0700 Subject: [PATCH 4/9] add comment --- relayer/chains/evm/provider.go | 92 ++++++++++++++++------------------ 1 file changed, 43 insertions(+), 49 deletions(-) diff --git a/relayer/chains/evm/provider.go b/relayer/chains/evm/provider.go index 0bdddd4..095fc53 100644 --- a/relayer/chains/evm/provider.go +++ b/relayer/chains/evm/provider.go @@ -303,24 +303,6 @@ func (cp *EVMChainProvider) RelayPacket(ctx context.Context, packet *bandtypes.P return fmt.Errorf("[EVMProvider] failed to relay packet after %d retries", cp.Config.MaxRetry) } -func (cp *EVMChainProvider) handleSaveTransaction(tx *db.Transaction, log logger.Logger, retryCount int) { - if err := cp.DB.AddOrUpdateTransaction(tx); err != nil { - log.Error("Save transaction error", "retry_count", retryCount, err) - cp.Alert.Trigger( - alert.NewTopic(alert.SaveDatabaseErrorMsg). - WithTunnelID(tx.TunnelID). - WithChainName(cp.ChainName). - GetFullTopic(), - err.Error(), - ) - } else { - cp.Alert.Reset(alert.NewTopic(alert.SaveDatabaseErrorMsg). - WithTunnelID(tx.TunnelID). - WithChainName(cp.ChainName). - GetFullTopic()) - } -} - // createAndSignRelayTx creates and signs the relay transaction. func (cp *EVMChainProvider) createAndSignRelayTx( ctx context.Context, @@ -426,6 +408,33 @@ func (cp *EVMChainProvider) handleMetrics(tunnelID uint64, createdAt time.Time, } } +// prepareUnconfirmedTransaction prepares the unconfirmed transaction to be stored in the database. +func (cp *EVMChainProvider) prepareUnconfirmedTransaction( + txHash string, + status types.TxStatus, + packet *bandtypes.Packet, + signerAddress string, +) *db.Transaction { + var signalPrices []db.SignalPrice + for _, p := range packet.SignalPrices { + signalPrices = append(signalPrices, *db.NewSignalPrice(p.SignalID, p.Price)) + } + + tx := db.NewUnconfirmedTransaction( + txHash, + packet.TunnelID, + packet.Sequence, + cp.ChainName, + types.ChainTypeEVM, + signerAddress, + status, + signalPrices, + ) + + return tx +} + +// prepareConfirmedTransaction prepares the confirmed transaction to be stored in the database. func (cp *EVMChainProvider) prepareConfirmedTransaction( signerAddress string, packet *bandtypes.Packet, @@ -434,10 +443,6 @@ func (cp *EVMChainProvider) prepareConfirmedTransaction( log logger.Logger, retryCount int, ) *db.Transaction { - if cp.DB == nil { - return nil - } - var signalPrices []db.SignalPrice for _, p := range packet.SignalPrices { @@ -504,34 +509,23 @@ func (cp *EVMChainProvider) prepareConfirmedTransaction( return tx } -func (cp *EVMChainProvider) prepareUnconfirmedTransaction( - txHash string, - status types.TxStatus, - packet *bandtypes.Packet, - signerAddress string, -) *db.Transaction { - // db was disabled - if cp.DB == nil { - return nil - } - - var signalPrices []db.SignalPrice - for _, p := range packet.SignalPrices { - signalPrices = append(signalPrices, *db.NewSignalPrice(p.SignalID, p.Price)) +// handleSaveTransaction saves the transaction to the database and triggers alert if any error occurs. +func (cp *EVMChainProvider) handleSaveTransaction(tx *db.Transaction, log logger.Logger, retryCount int) { + if err := cp.DB.AddOrUpdateTransaction(tx); err != nil { + log.Error("Save transaction error", "retry_count", retryCount, err) + cp.Alert.Trigger( + alert.NewTopic(alert.SaveDatabaseErrorMsg). + WithTunnelID(tx.TunnelID). + WithChainName(cp.ChainName). + GetFullTopic(), + err.Error(), + ) + } else { + cp.Alert.Reset(alert.NewTopic(alert.SaveDatabaseErrorMsg). + WithTunnelID(tx.TunnelID). + WithChainName(cp.ChainName). + GetFullTopic()) } - - tx := db.NewUnconfirmedTransaction( - txHash, - packet.TunnelID, - packet.Sequence, - cp.ChainName, - types.ChainTypeEVM, - signerAddress, - status, - signalPrices, - ) - - return tx } // CheckConfirmedTx checks the confirmed transaction status. From 7999c47abfc92a45d937e4a2bb4268e5895661e7 Mon Sep 17 00:00:00 2001 From: Tanut Lertwarachai Date: Fri, 21 Nov 2025 16:15:14 +0700 Subject: [PATCH 5/9] single prepare tx --- relayer/chains/evm/provider.go | 147 +++++++++++----------------- relayer/chains/evm/provider_test.go | 3 - relayer/chains/evm/types.go | 3 - relayer/db/types.go | 27 +---- 4 files changed, 61 insertions(+), 119 deletions(-) diff --git a/relayer/chains/evm/provider.go b/relayer/chains/evm/provider.go index 095fc53..1b19dd2 100644 --- a/relayer/chains/evm/provider.go +++ b/relayer/chains/evm/provider.go @@ -230,7 +230,7 @@ func (cp *EVMChainProvider) RelayPacket(ctx context.Context, packet *bandtypes.P // save pending tx in db if cp.DB != nil { - tx := cp.prepareUnconfirmedTransaction(txHash, types.TX_STATUS_PENDING, packet, freeSigner.GetAddress()) + tx := cp.prepareTransaction(txHash, freeSigner.GetAddress(), packet, nil, balance, log, retryCount) cp.handleSaveTransaction(tx, log, retryCount) } @@ -238,25 +238,15 @@ func (cp *EVMChainProvider) RelayPacket(ctx context.Context, packet *bandtypes.P cp.handleMetrics(packet.TunnelID, createdAt, txResult) if cp.DB != nil { - var tx *db.Transaction - switch txResult.Status { - case types.TX_STATUS_SUCCESS, types.TX_STATUS_FAILED: - tx = cp.prepareConfirmedTransaction( - freeSigner.GetAddress(), - packet, - txResult, - balance, - log, - retryCount, - ) - default: - tx = cp.prepareUnconfirmedTransaction( - txHash, - txResult.Status, - packet, - freeSigner.GetAddress(), - ) - } + tx := cp.prepareTransaction( + txHash, + freeSigner.GetAddress(), + packet, + &txResult, + balance, + log, + retryCount, + ) cp.handleSaveTransaction(tx, log, retryCount) } @@ -374,7 +364,6 @@ func (cp *EVMChainProvider) WaitForConfirmedTx( } return NewTxResult( - txHash, types.TX_STATUS_TIMEOUT, decimal.NullDecimal{}, decimal.NullDecimal{}, @@ -408,37 +397,12 @@ func (cp *EVMChainProvider) handleMetrics(tunnelID uint64, createdAt time.Time, } } -// prepareUnconfirmedTransaction prepares the unconfirmed transaction to be stored in the database. -func (cp *EVMChainProvider) prepareUnconfirmedTransaction( +// prepareTransaction prepares the transaction to be stored in the database. +func (cp *EVMChainProvider) prepareTransaction( txHash string, - status types.TxStatus, - packet *bandtypes.Packet, - signerAddress string, -) *db.Transaction { - var signalPrices []db.SignalPrice - for _, p := range packet.SignalPrices { - signalPrices = append(signalPrices, *db.NewSignalPrice(p.SignalID, p.Price)) - } - - tx := db.NewUnconfirmedTransaction( - txHash, - packet.TunnelID, - packet.Sequence, - cp.ChainName, - types.ChainTypeEVM, - signerAddress, - status, - signalPrices, - ) - - return tx -} - -// prepareConfirmedTransaction prepares the confirmed transaction to be stored in the database. -func (cp *EVMChainProvider) prepareConfirmedTransaction( signerAddress string, packet *bandtypes.Packet, - txResult TxResult, + txResult *TxResult, oldBalance *big.Int, log logger.Logger, retryCount int, @@ -449,58 +413,69 @@ func (cp *EVMChainProvider) prepareConfirmedTransaction( signalPrices = append(signalPrices, *db.NewSignalPrice(p.SignalID, p.Price)) } + txStatus := types.TX_STATUS_PENDING + gasUsed := decimal.NullDecimal{} + effectiveGasPrice := decimal.NullDecimal{} + balanceDelta := decimal.NullDecimal{} + var blockTimestamp *time.Time - block, err := cp.Client.GetBlock(context.Background(), txResult.BlockNumber) - if err != nil { - log.Error("Failed to get block", "retry_count", retryCount, err) - cp.Alert.Trigger( - alert.NewTopic(alert.GetBlockErrorMsg). - WithTunnelID(packet.TunnelID). - WithChainName(cp.ChainName). - GetFullTopic(), - err.Error(), - ) - } else { - timestamp := time.Unix(int64(block.Time()), 0).UTC() - blockTimestamp = ×tamp - cp.Alert.Reset(alert.NewTopic(alert.GetBlockErrorMsg).GetFullTopic()) - } - balanceDelta := decimal.NullDecimal{} - // Compute new balance - // Note: this may be incorrect if other transactions affected the user's balance during this period. - if oldBalance != nil { - newBalance, err := cp.Client.GetBalance( - context.Background(), - gethcommon.HexToAddress(signerAddress), - txResult.BlockNumber, - ) + if txResult != nil { + txStatus = txResult.Status + gasUsed = txResult.GasUsed + effectiveGasPrice = txResult.EffectiveGasPrice + + block, err := cp.Client.GetBlock(context.Background(), txResult.BlockNumber) if err != nil { - log.Error("Failed to get balance", "retry_count", retryCount, err) + log.Error("Failed to get block", "retry_count", retryCount, err) cp.Alert.Trigger( - alert.NewTopic(alert.GetBalanceErrorMsg). + alert.NewTopic(alert.GetBlockErrorMsg). WithTunnelID(packet.TunnelID). WithChainName(cp.ChainName). GetFullTopic(), err.Error(), ) } else { - diff := new(big.Int).Sub(newBalance, oldBalance) - balanceDelta = decimal.NewNullDecimal(decimal.NewFromBigInt(diff, 0)) - cp.Alert.Reset(alert.NewTopic(alert.GetBalanceErrorMsg).GetFullTopic()) + timestamp := time.Unix(int64(block.Time()), 0).UTC() + blockTimestamp = ×tamp + cp.Alert.Reset(alert.NewTopic(alert.GetBlockErrorMsg).GetFullTopic()) + } + + // Compute new balance + // Note: this may be incorrect if other transactions affected the user's balance during this period. + if oldBalance != nil { + newBalance, err := cp.Client.GetBalance( + context.Background(), + gethcommon.HexToAddress(signerAddress), + txResult.BlockNumber, + ) + if err != nil { + log.Error("Failed to get balance", "retry_count", retryCount, err) + cp.Alert.Trigger( + alert.NewTopic(alert.GetBalanceErrorMsg). + WithTunnelID(packet.TunnelID). + WithChainName(cp.ChainName). + GetFullTopic(), + err.Error(), + ) + } else { + diff := new(big.Int).Sub(newBalance, oldBalance) + balanceDelta = decimal.NewNullDecimal(decimal.NewFromBigInt(diff, 0)) + cp.Alert.Reset(alert.NewTopic(alert.GetBalanceErrorMsg).GetFullTopic()) + } } } - tx := db.NewConfirmedTransaction( - txResult.TxHash, + tx := db.NewTransaction( + txHash, packet.TunnelID, packet.Sequence, cp.ChainName, types.ChainTypeEVM, signerAddress, - txResult.Status, - txResult.GasUsed, - txResult.EffectiveGasPrice, + txStatus, + gasUsed, + effectiveGasPrice, balanceDelta, signalPrices, blockTimestamp, @@ -537,7 +512,6 @@ func (cp *EVMChainProvider) CheckConfirmedTx( if err != nil { err = fmt.Errorf("failed to get tx receipt: %w", err) return NewTxResult( - txHash, types.TX_STATUS_PENDING, decimal.NullDecimal{}, decimal.NullDecimal{}, @@ -552,7 +526,6 @@ func (cp *EVMChainProvider) CheckConfirmedTx( if receipt.Status == gethtypes.ReceiptStatusFailed { return NewTxResult( - txHash, types.TX_STATUS_FAILED, gasUsed, gasPrice, @@ -565,7 +538,6 @@ func (cp *EVMChainProvider) CheckConfirmedTx( if err != nil { err = fmt.Errorf("failed to get latest block height: %w", err) return NewTxResult( - txHash, types.TX_STATUS_PENDING, decimal.NullDecimal{}, decimal.NullDecimal{}, @@ -577,7 +549,6 @@ func (cp *EVMChainProvider) CheckConfirmedTx( // if tx block is not confirmed and waiting too long return status with timeout if receipt.BlockNumber.Uint64() > latestBlock-cp.Config.BlockConfirmation { return NewTxResult( - txHash, types.TX_STATUS_PENDING, decimal.NullDecimal{}, decimal.NullDecimal{}, @@ -586,7 +557,7 @@ func (cp *EVMChainProvider) CheckConfirmedTx( ), nil } - return NewTxResult(txHash, types.TX_STATUS_SUCCESS, gasUsed, gasPrice, receipt.BlockNumber, ""), nil + return NewTxResult(types.TX_STATUS_SUCCESS, gasUsed, gasPrice, receipt.BlockNumber, ""), nil } // EstimateGasFee estimates the gas for the transaction. diff --git a/relayer/chains/evm/provider_test.go b/relayer/chains/evm/provider_test.go index d6d3c84..c9c839a 100644 --- a/relayer/chains/evm/provider_test.go +++ b/relayer/chains/evm/provider_test.go @@ -233,7 +233,6 @@ func (s *ProviderTestSuite) TestCheckConfirmedTx() { s.client.EXPECT().GetBlockHeight(gomock.Any()).Return(uint64(currentBlock), nil) }, out: evm.NewTxResult( - txHash, chaintypes.TX_STATUS_SUCCESS, decimal.NewNullDecimal(decimal.New(21000, 0)), decimal.NewNullDecimal(decimal.New(20000, 0)), @@ -252,7 +251,6 @@ func (s *ProviderTestSuite) TestCheckConfirmedTx() { }, nil) }, out: evm.NewTxResult( - txHash, chaintypes.TX_STATUS_FAILED, decimal.NewNullDecimal(decimal.New(21000, 0)), decimal.NewNullDecimal(decimal.New(20000, 0)), @@ -274,7 +272,6 @@ func (s *ProviderTestSuite) TestCheckConfirmedTx() { s.client.EXPECT().GetBlockHeight(gomock.Any()).Return(uint64(currentBlock), nil) }, out: evm.NewTxResult( - txHash, chaintypes.TX_STATUS_PENDING, decimal.NullDecimal{}, decimal.NullDecimal{}, diff --git a/relayer/chains/evm/types.go b/relayer/chains/evm/types.go index 7a0c62a..ba6e678 100644 --- a/relayer/chains/evm/types.go +++ b/relayer/chains/evm/types.go @@ -37,7 +37,6 @@ type txResultMarshaling struct { // TxResult is the result of transaction. type TxResult struct { - TxHash string Status types.TxStatus GasUsed decimal.NullDecimal EffectiveGasPrice decimal.NullDecimal @@ -49,7 +48,6 @@ type TxResult struct { // NewTxResult creates a new TxResult instance. func NewTxResult( - txHash string, status types.TxStatus, gasUsed decimal.NullDecimal, effectiveGasPrice decimal.NullDecimal, @@ -57,7 +55,6 @@ func NewTxResult( failureReason string, ) TxResult { return TxResult{ - TxHash: txHash, Status: status, GasUsed: gasUsed, EffectiveGasPrice: effectiveGasPrice, diff --git a/relayer/db/types.go b/relayer/db/types.go index 6506bf5..49736ba 100644 --- a/relayer/db/types.go +++ b/relayer/db/types.go @@ -28,31 +28,8 @@ type Transaction struct { UpdatedAt time.Time } -// NewUnconfirmedTransaction creates a new pending Transaction instance. -func NewUnconfirmedTransaction( - txHash string, - tunnelID uint64, - sequence uint64, - chainName string, - chainType types.ChainType, - sender string, - status types.TxStatus, - signalPrices []SignalPrice, -) *Transaction { - return &Transaction{ - TxHash: txHash, - TunnelID: tunnelID, - Sequence: sequence, - ChainName: chainName, - ChainType: chainType, - Sender: sender, - Status: status, - SignalPrices: signalPrices, - } -} - -// NewConfirmedTransaction creates a new Transaction instance. -func NewConfirmedTransaction( +// NewTransaction creates a new Transaction instance. +func NewTransaction( txHash string, tunnelID uint64, sequence uint64, From d8e505498f84732393128e3edb512cb23160d1b0 Mon Sep 17 00:00:00 2001 From: Tanut Lertwarachai Date: Fri, 21 Nov 2025 17:48:55 +0700 Subject: [PATCH 6/9] change alert logic --- relayer/chains/evm/provider.go | 57 ++++++++++++++-------------------- 1 file changed, 23 insertions(+), 34 deletions(-) diff --git a/relayer/chains/evm/provider.go b/relayer/chains/evm/provider.go index 1b19dd2..54b42c0 100644 --- a/relayer/chains/evm/provider.go +++ b/relayer/chains/evm/provider.go @@ -194,15 +194,13 @@ func (cp *EVMChainProvider) RelayPacket(ctx context.Context, packet *bandtypes.P balance, err = cp.Client.GetBalance(ctx, gethcommon.HexToAddress(freeSigner.GetAddress()), nil) if err != nil { log.Error("Failed to get balance", "retry_count", retryCount, err) - cp.Alert.Trigger( - alert.NewTopic(alert.GetBalanceErrorMsg). - WithTunnelID(packet.TunnelID). - WithChainName(cp.ChainName). - GetFullTopic(), - err.Error(), - ) + alert.HandleAlert(cp.Alert, alert.NewTopic(alert.GetBalanceErrorMsg). + WithTunnelID(packet.TunnelID). + WithChainName(cp.ChainName), err.Error()) } else { - cp.Alert.Reset(alert.NewTopic(alert.GetBalanceErrorMsg).GetFullTopic()) + alert.HandleReset(cp.Alert, alert.NewTopic(alert.GetBalanceErrorMsg). + WithTunnelID(packet.TunnelID). + WithChainName(cp.ChainName)) } } @@ -428,17 +426,15 @@ func (cp *EVMChainProvider) prepareTransaction( block, err := cp.Client.GetBlock(context.Background(), txResult.BlockNumber) if err != nil { log.Error("Failed to get block", "retry_count", retryCount, err) - cp.Alert.Trigger( - alert.NewTopic(alert.GetBlockErrorMsg). - WithTunnelID(packet.TunnelID). - WithChainName(cp.ChainName). - GetFullTopic(), - err.Error(), - ) + alert.HandleAlert(cp.Alert, alert.NewTopic(alert.GetBlockErrorMsg). + WithTunnelID(packet.TunnelID). + WithChainName(cp.ChainName), err.Error()) } else { timestamp := time.Unix(int64(block.Time()), 0).UTC() blockTimestamp = ×tamp - cp.Alert.Reset(alert.NewTopic(alert.GetBlockErrorMsg).GetFullTopic()) + alert.HandleReset(cp.Alert, alert.NewTopic(alert.GetBlockErrorMsg). + WithTunnelID(packet.TunnelID). + WithChainName(cp.ChainName)) } // Compute new balance @@ -451,17 +447,15 @@ func (cp *EVMChainProvider) prepareTransaction( ) if err != nil { log.Error("Failed to get balance", "retry_count", retryCount, err) - cp.Alert.Trigger( - alert.NewTopic(alert.GetBalanceErrorMsg). - WithTunnelID(packet.TunnelID). - WithChainName(cp.ChainName). - GetFullTopic(), - err.Error(), - ) + alert.HandleAlert(cp.Alert, alert.NewTopic(alert.GetBalanceErrorMsg). + WithTunnelID(packet.TunnelID). + WithChainName(cp.ChainName), err.Error()) } else { diff := new(big.Int).Sub(newBalance, oldBalance) balanceDelta = decimal.NewNullDecimal(decimal.NewFromBigInt(diff, 0)) - cp.Alert.Reset(alert.NewTopic(alert.GetBalanceErrorMsg).GetFullTopic()) + alert.HandleReset(cp.Alert, alert.NewTopic(alert.GetBalanceErrorMsg). + WithTunnelID(packet.TunnelID). + WithChainName(cp.ChainName)) } } } @@ -488,18 +482,13 @@ func (cp *EVMChainProvider) prepareTransaction( func (cp *EVMChainProvider) handleSaveTransaction(tx *db.Transaction, log logger.Logger, retryCount int) { if err := cp.DB.AddOrUpdateTransaction(tx); err != nil { log.Error("Save transaction error", "retry_count", retryCount, err) - cp.Alert.Trigger( - alert.NewTopic(alert.SaveDatabaseErrorMsg). - WithTunnelID(tx.TunnelID). - WithChainName(cp.ChainName). - GetFullTopic(), - err.Error(), - ) + alert.HandleAlert(cp.Alert, alert.NewTopic(alert.SaveDatabaseErrorMsg). + WithTunnelID(tx.TunnelID). + WithChainName(cp.ChainName), err.Error()) } else { - cp.Alert.Reset(alert.NewTopic(alert.SaveDatabaseErrorMsg). + alert.HandleReset(cp.Alert, alert.NewTopic(alert.SaveDatabaseErrorMsg). WithTunnelID(tx.TunnelID). - WithChainName(cp.ChainName). - GetFullTopic()) + WithChainName(cp.ChainName)) } } From 40d898eead4f41a72f794ae98d825a676333f0e6 Mon Sep 17 00:00:00 2001 From: Tanut Lertwarachai Date: Fri, 21 Nov 2025 18:02:00 +0700 Subject: [PATCH 7/9] fix copilot comment --- relayer/alert/alert.go | 2 +- relayer/chains/evm/provider.go | 56 ++++++++++++++++++---------------- 2 files changed, 30 insertions(+), 28 deletions(-) diff --git a/relayer/alert/alert.go b/relayer/alert/alert.go index 0944dc7..e462887 100644 --- a/relayer/alert/alert.go +++ b/relayer/alert/alert.go @@ -12,7 +12,7 @@ const ( GetContractTunnelInfoErrorMsg = "Failed to get tunnel info from contract" PacketSigningStatusErrorMsg = "Failed tunnel packet signing status" GetBlockErrorMsg = "Failed to get block from chain" - GetBalanceErrorMsg = "Failed to prepare database transaction" + GetBalanceErrorMsg = "Failed to get balance from chain" SaveDatabaseErrorMsg = "Failed to save to database" ) diff --git a/relayer/chains/evm/provider.go b/relayer/chains/evm/provider.go index 54b42c0..3f54b15 100644 --- a/relayer/chains/evm/provider.go +++ b/relayer/chains/evm/provider.go @@ -423,40 +423,42 @@ func (cp *EVMChainProvider) prepareTransaction( gasUsed = txResult.GasUsed effectiveGasPrice = txResult.EffectiveGasPrice - block, err := cp.Client.GetBlock(context.Background(), txResult.BlockNumber) - if err != nil { - log.Error("Failed to get block", "retry_count", retryCount, err) - alert.HandleAlert(cp.Alert, alert.NewTopic(alert.GetBlockErrorMsg). - WithTunnelID(packet.TunnelID). - WithChainName(cp.ChainName), err.Error()) - } else { - timestamp := time.Unix(int64(block.Time()), 0).UTC() - blockTimestamp = ×tamp - alert.HandleReset(cp.Alert, alert.NewTopic(alert.GetBlockErrorMsg). - WithTunnelID(packet.TunnelID). - WithChainName(cp.ChainName)) - } - - // Compute new balance - // Note: this may be incorrect if other transactions affected the user's balance during this period. - if oldBalance != nil { - newBalance, err := cp.Client.GetBalance( - context.Background(), - gethcommon.HexToAddress(signerAddress), - txResult.BlockNumber, - ) + if txResult.Status == types.TX_STATUS_SUCCESS || txResult.Status == types.TX_STATUS_FAILED { + block, err := cp.Client.GetBlock(context.Background(), txResult.BlockNumber) if err != nil { - log.Error("Failed to get balance", "retry_count", retryCount, err) - alert.HandleAlert(cp.Alert, alert.NewTopic(alert.GetBalanceErrorMsg). + log.Error("Failed to get block", "retry_count", retryCount, err) + alert.HandleAlert(cp.Alert, alert.NewTopic(alert.GetBlockErrorMsg). WithTunnelID(packet.TunnelID). WithChainName(cp.ChainName), err.Error()) } else { - diff := new(big.Int).Sub(newBalance, oldBalance) - balanceDelta = decimal.NewNullDecimal(decimal.NewFromBigInt(diff, 0)) - alert.HandleReset(cp.Alert, alert.NewTopic(alert.GetBalanceErrorMsg). + timestamp := time.Unix(int64(block.Time()), 0).UTC() + blockTimestamp = ×tamp + alert.HandleReset(cp.Alert, alert.NewTopic(alert.GetBlockErrorMsg). WithTunnelID(packet.TunnelID). WithChainName(cp.ChainName)) } + + // Compute new balance + // Note: this may be incorrect if other transactions affected the user's balance during this period. + if oldBalance != nil { + newBalance, err := cp.Client.GetBalance( + context.Background(), + gethcommon.HexToAddress(signerAddress), + txResult.BlockNumber, + ) + if err != nil { + log.Error("Failed to get balance", "retry_count", retryCount, err) + alert.HandleAlert(cp.Alert, alert.NewTopic(alert.GetBalanceErrorMsg). + WithTunnelID(packet.TunnelID). + WithChainName(cp.ChainName), err.Error()) + } else { + diff := new(big.Int).Sub(newBalance, oldBalance) + balanceDelta = decimal.NewNullDecimal(decimal.NewFromBigInt(diff, 0)) + alert.HandleReset(cp.Alert, alert.NewTopic(alert.GetBalanceErrorMsg). + WithTunnelID(packet.TunnelID). + WithChainName(cp.ChainName)) + } + } } } From 95236a36e8aa5e449eabecba751519d782030fff Mon Sep 17 00:00:00 2001 From: Tanut Lertwarachai Date: Mon, 24 Nov 2025 14:08:32 +0700 Subject: [PATCH 8/9] fix copilot comment --- relayer/chains/evm/provider.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/relayer/chains/evm/provider.go b/relayer/chains/evm/provider.go index 3f54b15..84b7e1c 100644 --- a/relayer/chains/evm/provider.go +++ b/relayer/chains/evm/provider.go @@ -228,7 +228,7 @@ func (cp *EVMChainProvider) RelayPacket(ctx context.Context, packet *bandtypes.P // save pending tx in db if cp.DB != nil { - tx := cp.prepareTransaction(txHash, freeSigner.GetAddress(), packet, nil, balance, log, retryCount) + tx := cp.prepareTransaction(ctx, txHash, freeSigner.GetAddress(), packet, nil, balance, log, retryCount) cp.handleSaveTransaction(tx, log, retryCount) } @@ -237,6 +237,7 @@ func (cp *EVMChainProvider) RelayPacket(ctx context.Context, packet *bandtypes.P cp.handleMetrics(packet.TunnelID, createdAt, txResult) if cp.DB != nil { tx := cp.prepareTransaction( + ctx, txHash, freeSigner.GetAddress(), packet, @@ -397,6 +398,7 @@ func (cp *EVMChainProvider) handleMetrics(tunnelID uint64, createdAt time.Time, // prepareTransaction prepares the transaction to be stored in the database. func (cp *EVMChainProvider) prepareTransaction( + ctx context.Context, txHash string, signerAddress string, packet *bandtypes.Packet, @@ -442,7 +444,7 @@ func (cp *EVMChainProvider) prepareTransaction( // Note: this may be incorrect if other transactions affected the user's balance during this period. if oldBalance != nil { newBalance, err := cp.Client.GetBalance( - context.Background(), + ctx, gethcommon.HexToAddress(signerAddress), txResult.BlockNumber, ) From 8dc3a5ad0c32c95c5400c5db7e9cd960845e4431 Mon Sep 17 00:00:00 2001 From: Tanut Lertwarachai Date: Mon, 24 Nov 2025 14:13:48 +0700 Subject: [PATCH 9/9] check db nil in handlesavetx --- relayer/chains/evm/provider.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/relayer/chains/evm/provider.go b/relayer/chains/evm/provider.go index 84b7e1c..8466c93 100644 --- a/relayer/chains/evm/provider.go +++ b/relayer/chains/evm/provider.go @@ -484,6 +484,10 @@ func (cp *EVMChainProvider) prepareTransaction( // handleSaveTransaction saves the transaction to the database and triggers alert if any error occurs. func (cp *EVMChainProvider) handleSaveTransaction(tx *db.Transaction, log logger.Logger, retryCount int) { + if cp.DB == nil { + log.Debug("Database is not set; skipping saving transaction") + return + } if err := cp.DB.AddOrUpdateTransaction(tx); err != nil { log.Error("Save transaction error", "retry_count", retryCount, err) alert.HandleAlert(cp.Alert, alert.NewTopic(alert.SaveDatabaseErrorMsg).