From 59e44af04dba67089452a60eefdb0b53cfc479de Mon Sep 17 00:00:00 2001 From: faisal-link Date: Fri, 16 Jan 2026 21:19:10 +0400 Subject: [PATCH 1/5] initial setup for metrics reporting --- relayer/chainreader/indexer/events_indexer.go | 13 + relayer/chainreader/indexer/indexer.go | 35 +++ .../indexer/transactions_indexer.go | 13 + relayer/chainreader/reader/chainreader.go | 17 ++ relayer/chainwriter/chainwriter.go | 17 ++ relayer/monitor/health_metrics.go | 207 ++++++++++++++ relayer/monitor/health_metrics_test.go | 270 ++++++++++++++++++ relayer/plugin/relayer.go | 83 +++++- relayer/txm/confirmer.go | 7 +- relayer/txm/txm.go | 17 ++ 10 files changed, 676 insertions(+), 3 deletions(-) create mode 100644 relayer/monitor/health_metrics.go create mode 100644 relayer/monitor/health_metrics_test.go diff --git a/relayer/chainreader/indexer/events_indexer.go b/relayer/chainreader/indexer/events_indexer.go index 8b5fd053c..3a79d2df4 100644 --- a/relayer/chainreader/indexer/events_indexer.go +++ b/relayer/chainreader/indexer/events_indexer.go @@ -37,6 +37,9 @@ type EventsIndexer struct { // a map of event handles to the last processed cursor lastProcessedCursors map[string]*models.EventId cursorMutex sync.RWMutex + + // Optional callback for recording successful sync operations + onSyncSuccess func(ctx context.Context) } type EventsIndexerApi interface { @@ -45,6 +48,7 @@ type EventsIndexerApi interface { SyncEvent(ctx context.Context, selector *client.EventSelector) error AddEventSelector(ctx context.Context, selector *client.EventSelector) error SetEventOffsetOverrides(ctx context.Context, offsetOverrides map[string]client.EventId) error + SetOnSyncSuccess(callback func(ctx context.Context)) Ready() error Close() error } @@ -92,6 +96,10 @@ func (eIndexer *EventsIndexer) Start(ctx context.Context) error { eIndexer.logger.Warnw("EventSync timed out", "duration", elapsed) } else { eIndexer.logger.Debugw("Event sync completed successfully", "duration", elapsed) + // Record successful sync for health metrics + if eIndexer.onSyncSuccess != nil { + eIndexer.onSyncSuccess(ctx) + } } cancel() @@ -451,3 +459,8 @@ func (eIndexer *EventsIndexer) Close() error { // TODO: implement return nil } + +// SetOnSyncSuccess sets a callback function that is called after a successful sync operation. +func (eIndexer *EventsIndexer) SetOnSyncSuccess(callback func(ctx context.Context)) { + eIndexer.onSyncSuccess = callback +} diff --git a/relayer/chainreader/indexer/indexer.go b/relayer/chainreader/indexer/indexer.go index 280a02308..4bb451502 100644 --- a/relayer/chainreader/indexer/indexer.go +++ b/relayer/chainreader/indexer/indexer.go @@ -7,6 +7,8 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" + + "github.com/smartcontractkit/chainlink-sui/relayer/monitor" ) type Indexer struct { @@ -22,6 +24,9 @@ type Indexer struct { transactionIndexerErr atomic.Value // stores error from transaction indexer goroutine wg sync.WaitGroup // wait for both indexer goroutines to exit + + // Health metrics for monitoring (optional) + healthMetrics *monitor.HealthMetrics } type IndexerApi interface { @@ -54,6 +59,16 @@ func (i *Indexer) Name() string { func (i *Indexer) Start(_ context.Context) error { return i.starter.StartOnce(i.Name(), func() error { + // Set up health metrics callbacks if health metrics are configured + if i.healthMetrics != nil { + i.eventsIndexer.SetOnSyncSuccess(func(ctx context.Context) { + i.RecordEventsIndexerSuccess(ctx) + }) + i.transactionIndexer.SetOnSyncSuccess(func(ctx context.Context) { + i.RecordTransactionsIndexerSuccess(ctx) + }) + } + // Events indexer eventsIndexerCtx, eventsIndexerCancel := context.WithCancel(context.Background()) i.eventsIndexerCancel = &eventsIndexerCancel @@ -158,3 +173,23 @@ func (i *Indexer) GetTransactionIndexer() TransactionsIndexerApi { } return i.transactionIndexer } + +// SetHealthMetrics sets the health metrics instance for the indexer. +// This should be called after creating the indexer to enable health metrics reporting. +func (i *Indexer) SetHealthMetrics(hm *monitor.HealthMetrics) { + i.healthMetrics = hm +} + +// RecordEventsIndexerSuccess records a successful events indexer sync operation. +func (i *Indexer) RecordEventsIndexerSuccess(ctx context.Context) { + if i.healthMetrics != nil { + i.healthMetrics.RecordLastSuccess(ctx, monitor.ComponentEventsIndexer) + } +} + +// RecordTransactionsIndexerSuccess records a successful transactions indexer sync operation. +func (i *Indexer) RecordTransactionsIndexerSuccess(ctx context.Context) { + if i.healthMetrics != nil { + i.healthMetrics.RecordLastSuccess(ctx, monitor.ComponentTransactionsIndexer) + } +} diff --git a/relayer/chainreader/indexer/transactions_indexer.go b/relayer/chainreader/indexer/transactions_indexer.go index 7658819d8..e323e4835 100644 --- a/relayer/chainreader/indexer/transactions_indexer.go +++ b/relayer/chainreader/indexer/transactions_indexer.go @@ -50,11 +50,15 @@ type TransactionsIndexer struct { mu sync.RWMutex offrampPackageIdReady chan struct{} offrampPackageOnce sync.Once + + // Optional callback for recording successful sync operations + onSyncSuccess func(ctx context.Context) } type TransactionsIndexerApi interface { Start(ctx context.Context) error SetOffRampPackage(pkg string, latestPkg string) + SetOnSyncSuccess(callback func(ctx context.Context)) Ready() error Close() error } @@ -115,6 +119,10 @@ func (tIndexer *TransactionsIndexer) Start(ctx context.Context) error { tIndexer.logger.Warnw("Transaction sync timed out", "duration", elapsed) } else { tIndexer.logger.Debugw("Transaction sync completed successfully", "duration", elapsed) + // Record successful sync for health metrics + if tIndexer.onSyncSuccess != nil { + tIndexer.onSyncSuccess(ctx) + } } cancel() @@ -837,3 +845,8 @@ func (tIndexer *TransactionsIndexer) Close() error { // TODO: implement return nil } + +// SetOnSyncSuccess sets a callback function that is called after a successful sync operation. +func (tIndexer *TransactionsIndexer) SetOnSyncSuccess(callback func(ctx context.Context)) { + tIndexer.onSyncSuccess = callback +} diff --git a/relayer/chainreader/reader/chainreader.go b/relayer/chainreader/reader/chainreader.go index eefc77c99..82c2c3e44 100644 --- a/relayer/chainreader/reader/chainreader.go +++ b/relayer/chainreader/reader/chainreader.go @@ -28,6 +28,7 @@ import ( "github.com/smartcontractkit/chainlink-sui/relayer/client" "github.com/smartcontractkit/chainlink-sui/relayer/codec" "github.com/smartcontractkit/chainlink-sui/relayer/common" + "github.com/smartcontractkit/chainlink-sui/relayer/monitor" "github.com/smartcontractkit/chainlink-common/pkg/logger" pkgtypes "github.com/smartcontractkit/chainlink-common/pkg/types" @@ -58,6 +59,9 @@ type suiChainReader struct { // Value: parent object ID parentObjectIDs map[string]string parentObjectIDsMutex sync.RWMutex + + // Health metrics for monitoring (optional) + healthMetrics *monitor.HealthMetrics } var _ pkgtypes.ContractTypeProvider = &suiChainReader{} @@ -155,6 +159,19 @@ func (s *suiChainReader) Close() error { }) } +// SetHealthMetrics sets the health metrics instance for the chain reader. +// This should be called after creating the chain reader to enable health metrics reporting. +func (s *suiChainReader) SetHealthMetrics(hm *monitor.HealthMetrics) { + s.healthMetrics = hm +} + +// recordLastSuccess records a successful operation to the health metrics. +func (s *suiChainReader) recordLastSuccess(ctx context.Context) { + if s.healthMetrics != nil { + s.healthMetrics.RecordLastSuccess(ctx, monitor.ComponentChainReader) + } +} + func (s *suiChainReader) Bind(ctx context.Context, bindings []pkgtypes.BoundContract) error { offrampPackageAddress := "" diff --git a/relayer/chainwriter/chainwriter.go b/relayer/chainwriter/chainwriter.go index 8a4c9dec4..e0d430a12 100644 --- a/relayer/chainwriter/chainwriter.go +++ b/relayer/chainwriter/chainwriter.go @@ -14,6 +14,7 @@ import ( cwConfig "github.com/smartcontractkit/chainlink-sui/relayer/chainwriter/config" "github.com/smartcontractkit/chainlink-sui/relayer/chainwriter/ptb" "github.com/smartcontractkit/chainlink-sui/relayer/chainwriter/ptb/offramp" + "github.com/smartcontractkit/chainlink-sui/relayer/monitor" "github.com/smartcontractkit/chainlink-sui/relayer/txm" ) @@ -29,6 +30,9 @@ type SuiChainWriter struct { simulate bool ptbFactory *ptb.PTBConstructor services.StateMachine + + // Health metrics for monitoring (optional) + healthMetrics *monitor.HealthMetrics } func NewSuiChainWriter(lggr logger.Logger, txManager txm.TxManager, config cwConfig.ChainWriterConfig, simulate bool) (*SuiChainWriter, error) { @@ -197,6 +201,19 @@ func (s *SuiChainWriter) Start(ctx context.Context) error { }) } +// SetHealthMetrics sets the health metrics instance for the chain writer. +// This should be called after creating the chain writer to enable health metrics reporting. +func (s *SuiChainWriter) SetHealthMetrics(hm *monitor.HealthMetrics) { + s.healthMetrics = hm +} + +// recordLastSuccess records a successful operation to the health metrics. +func (s *SuiChainWriter) recordLastSuccess(ctx context.Context) { + if s.healthMetrics != nil { + s.healthMetrics.RecordLastSuccess(ctx, monitor.ComponentChainWriter) + } +} + func (s *SuiChainWriter) EstimateGasBudgetFromCCIPExecuteMessage(ctx context.Context, arguments map[string]any, meta *commonTypes.TxMeta) (*big.Int, error) { offrampArgs, err := offramp.DecodeOffRampExecCallArgs(arguments) if err != nil { diff --git a/relayer/monitor/health_metrics.go b/relayer/monitor/health_metrics.go new file mode 100644 index 000000000..cfe040e53 --- /dev/null +++ b/relayer/monitor/health_metrics.go @@ -0,0 +1,207 @@ +package monitor + +import ( + "context" + "fmt" + "sync" + "time" + + "github.com/smartcontractkit/chainlink-sui/relayer/config" + + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/metric" + + "github.com/smartcontractkit/chainlink-common/pkg/beholder" + + "github.com/smartcontractkit/chainlink-aptos/relayer/monitoring/metric/utils" +) + +// Component names for health metrics +const ( + ComponentSuiRelayer = "SuiRelayer" + ComponentTxM = "TxM" + ComponentEventsIndexer = "EventsIndexer" + ComponentTransactionsIndexer = "TransactionsIndexer" + ComponentChainReader = "ChainReader" + ComponentChainWriter = "ChainWriter" +) + +// HealthMetrics provides per-component health metrics that are pushed to Grafana. +// It tracks component status, flip-flop counts, and processing lag for monitoring +// and alerting purposes. +type HealthMetrics struct { + chainInfo config.ChainInfo + + // Metrics + statusGauge metric.Int64Gauge + flipFlopCounter metric.Int64Counter + lastSuccessTimestampGauge metric.Int64Gauge + processingLagGauge metric.Float64Gauge + + // Internal state for tracking flip-flops + mu sync.RWMutex + lastHealthStatus map[string]bool + lastSuccessTime map[string]time.Time +} + +// NewHealthMetrics creates a new HealthMetrics instance and pre-registers all metrics. +// Metrics are registered at startup so they are always present for alerting. +func NewHealthMetrics(chainInfo config.ChainInfo) (*HealthMetrics, error) { + meter := beholder.GetMeter() + + statusGauge, err := meter.Int64Gauge( + "sui_component_status", + metric.WithDescription("Component health status: 1=healthy, 0=unhealthy"), + metric.WithUnit("1"), + ) + if err != nil { + return nil, fmt.Errorf("failed to create status gauge: %w", err) + } + + flipFlopCounter, err := meter.Int64Counter( + "sui_component_flip_flops_total", + metric.WithDescription("Number of healthy/unhealthy state transitions"), + metric.WithUnit("1"), + ) + if err != nil { + return nil, fmt.Errorf("failed to create flip-flop counter: %w", err) + } + + lastSuccessTimestampGauge, err := meter.Int64Gauge( + "sui_component_last_success_timestamp", + metric.WithDescription("Unix timestamp of last successful operation"), + metric.WithUnit("s"), + ) + if err != nil { + return nil, fmt.Errorf("failed to create last success timestamp gauge: %w", err) + } + + processingLagGauge, err := meter.Float64Gauge( + "sui_component_processing_lag_seconds", + metric.WithDescription("Time since last successful operation in seconds"), + metric.WithUnit("s"), + ) + if err != nil { + return nil, fmt.Errorf("failed to create processing lag gauge: %w", err) + } + + hm := &HealthMetrics{ + chainInfo: chainInfo, + statusGauge: statusGauge, + flipFlopCounter: flipFlopCounter, + lastSuccessTimestampGauge: lastSuccessTimestampGauge, + processingLagGauge: processingLagGauge, + lastHealthStatus: make(map[string]bool), + lastSuccessTime: make(map[string]time.Time), + } + + // Pre-register all component metrics with initial values + components := []string{ + ComponentSuiRelayer, + ComponentTxM, + ComponentEventsIndexer, + ComponentTransactionsIndexer, + ComponentChainReader, + ComponentChainWriter, + } + + ctx := context.Background() + now := time.Now() + for _, component := range components { + // Initialize with unknown/unhealthy state + hm.lastHealthStatus[component] = false + hm.lastSuccessTime[component] = now + + // Record initial metrics + attrs := hm.getAttributes(component) + hm.statusGauge.Record(ctx, 0, metric.WithAttributeSet(attrs)) + hm.lastSuccessTimestampGauge.Record(ctx, now.Unix(), metric.WithAttributeSet(attrs)) + hm.processingLagGauge.Record(ctx, 0, metric.WithAttributeSet(attrs)) + } + + return hm, nil +} + +// RecordHealth records the health status of a component and tracks flip-flops. +func (hm *HealthMetrics) RecordHealth(ctx context.Context, component string, healthy bool) { + hm.mu.Lock() + defer hm.mu.Unlock() + + attrs := hm.getAttributes(component) + + // Check for flip-flop (state change) + if lastStatus, exists := hm.lastHealthStatus[component]; exists && lastStatus != healthy { + hm.flipFlopCounter.Add(ctx, 1, metric.WithAttributeSet(attrs)) + } + hm.lastHealthStatus[component] = healthy + + // Record current status + var statusValue int64 + if healthy { + statusValue = 1 + } + hm.statusGauge.Record(ctx, statusValue, metric.WithAttributeSet(attrs)) +} + +// RecordLastSuccess records the timestamp of a successful operation and updates +// the processing lag metric. +func (hm *HealthMetrics) RecordLastSuccess(ctx context.Context, component string) { + hm.mu.Lock() + defer hm.mu.Unlock() + + now := time.Now() + hm.lastSuccessTime[component] = now + + attrs := hm.getAttributes(component) + hm.lastSuccessTimestampGauge.Record(ctx, now.Unix(), metric.WithAttributeSet(attrs)) + hm.processingLagGauge.Record(ctx, 0, metric.WithAttributeSet(attrs)) +} + +// UpdateProcessingLag updates the processing lag for all components based on their +// last success time. This should be called periodically. +func (hm *HealthMetrics) UpdateProcessingLag(ctx context.Context) { + hm.mu.RLock() + defer hm.mu.RUnlock() + + now := time.Now() + for component, lastSuccess := range hm.lastSuccessTime { + lag := now.Sub(lastSuccess).Seconds() + attrs := hm.getAttributes(component) + hm.processingLagGauge.Record(ctx, lag, metric.WithAttributeSet(attrs)) + } +} + +// RecordHealthFromReport processes a HealthReport map and records metrics for all components. +func (hm *HealthMetrics) RecordHealthFromReport(ctx context.Context, report map[string]error) { + for component, err := range report { + healthy := err == nil + hm.RecordHealth(ctx, component, healthy) + } +} + +// getAttributes returns the OpenTelemetry attributes for a component. +func (hm *HealthMetrics) getAttributes(component string) attribute.Set { + return attribute.NewSet( + attribute.String("component", component), + attribute.String("chain_family_name", utils.ValOrUnknown(hm.chainInfo.ChainFamilyName)), + attribute.String("chain_id", utils.ValOrUnknown(hm.chainInfo.ChainID)), + attribute.String("network_name", utils.ValOrUnknown(hm.chainInfo.NetworkName)), + attribute.String("network_name_full", utils.ValOrUnknown(hm.chainInfo.NetworkNameFull)), + ) +} + +// GetLastSuccessTime returns the last success time for a component. +func (hm *HealthMetrics) GetLastSuccessTime(component string) (time.Time, bool) { + hm.mu.RLock() + defer hm.mu.RUnlock() + t, ok := hm.lastSuccessTime[component] + return t, ok +} + +// GetHealthStatus returns the last known health status for a component. +func (hm *HealthMetrics) GetHealthStatus(component string) (bool, bool) { + hm.mu.RLock() + defer hm.mu.RUnlock() + status, ok := hm.lastHealthStatus[component] + return status, ok +} diff --git a/relayer/monitor/health_metrics_test.go b/relayer/monitor/health_metrics_test.go new file mode 100644 index 000000000..36aa620f9 --- /dev/null +++ b/relayer/monitor/health_metrics_test.go @@ -0,0 +1,270 @@ +package monitor + +import ( + "context" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink-sui/relayer/config" +) + +func TestNewHealthMetrics(t *testing.T) { + t.Parallel() + + chainInfo := config.ChainInfo{ + ChainFamilyName: "sui", + ChainID: "test-chain-id", + NetworkName: "testnet", + NetworkNameFull: "Sui Testnet", + } + + hm, err := NewHealthMetrics(chainInfo) + require.NoError(t, err) + require.NotNil(t, hm) + + // Verify all component metrics are initialized + components := []string{ + ComponentSuiRelayer, + ComponentTxM, + ComponentEventsIndexer, + ComponentTransactionsIndexer, + ComponentChainReader, + ComponentChainWriter, + } + + for _, component := range components { + // Check that initial health status is set to false (unhealthy) + status, exists := hm.GetHealthStatus(component) + assert.True(t, exists, "Component %s should have initial status", component) + assert.False(t, status, "Initial status for %s should be unhealthy", component) + + // Check that initial last success time is set + lastSuccess, exists := hm.GetLastSuccessTime(component) + assert.True(t, exists, "Component %s should have initial last success time", component) + assert.False(t, lastSuccess.IsZero(), "Last success time for %s should not be zero", component) + } +} + +func TestRecordHealth(t *testing.T) { + t.Parallel() + + chainInfo := config.ChainInfo{ + ChainFamilyName: "sui", + ChainID: "test-chain-id", + NetworkName: "testnet", + NetworkNameFull: "Sui Testnet", + } + + hm, err := NewHealthMetrics(chainInfo) + require.NoError(t, err) + + ctx := context.Background() + component := ComponentTxM + + // Initial state should be unhealthy + status, exists := hm.GetHealthStatus(component) + assert.True(t, exists) + assert.False(t, status) + + // Record healthy status + hm.RecordHealth(ctx, component, true) + status, exists = hm.GetHealthStatus(component) + assert.True(t, exists) + assert.True(t, status) + + // Record unhealthy status (flip-flop) + hm.RecordHealth(ctx, component, false) + status, exists = hm.GetHealthStatus(component) + assert.True(t, exists) + assert.False(t, status) + + // Record healthy again (another flip-flop) + hm.RecordHealth(ctx, component, true) + status, exists = hm.GetHealthStatus(component) + assert.True(t, exists) + assert.True(t, status) +} + +func TestRecordLastSuccess(t *testing.T) { + t.Parallel() + + chainInfo := config.ChainInfo{ + ChainFamilyName: "sui", + ChainID: "test-chain-id", + NetworkName: "testnet", + NetworkNameFull: "Sui Testnet", + } + + hm, err := NewHealthMetrics(chainInfo) + require.NoError(t, err) + + ctx := context.Background() + component := ComponentEventsIndexer + + // Get initial last success time + initialTime, exists := hm.GetLastSuccessTime(component) + require.True(t, exists) + + // Wait a bit to ensure time difference + time.Sleep(10 * time.Millisecond) + + // Record a new success + hm.RecordLastSuccess(ctx, component) + + // Verify last success time was updated + newTime, exists := hm.GetLastSuccessTime(component) + require.True(t, exists) + assert.True(t, newTime.After(initialTime), "Last success time should be updated") +} + +func TestRecordHealthFromReport(t *testing.T) { + t.Parallel() + + chainInfo := config.ChainInfo{ + ChainFamilyName: "sui", + ChainID: "test-chain-id", + NetworkName: "testnet", + NetworkNameFull: "Sui Testnet", + } + + hm, err := NewHealthMetrics(chainInfo) + require.NoError(t, err) + + ctx := context.Background() + + // Simulate a health report with mixed statuses + report := map[string]error{ + ComponentSuiRelayer: nil, // healthy + ComponentTxM: nil, // healthy + ComponentEventsIndexer: assert.AnError, // unhealthy + ComponentChainReader: nil, // healthy + } + + hm.RecordHealthFromReport(ctx, report) + + // Verify statuses were recorded correctly + status, exists := hm.GetHealthStatus(ComponentSuiRelayer) + assert.True(t, exists) + assert.True(t, status) + + status, exists = hm.GetHealthStatus(ComponentTxM) + assert.True(t, exists) + assert.True(t, status) + + status, exists = hm.GetHealthStatus(ComponentEventsIndexer) + assert.True(t, exists) + assert.False(t, status) + + status, exists = hm.GetHealthStatus(ComponentChainReader) + assert.True(t, exists) + assert.True(t, status) +} + +func TestUpdateProcessingLag(t *testing.T) { + t.Parallel() + + chainInfo := config.ChainInfo{ + ChainFamilyName: "sui", + ChainID: "test-chain-id", + NetworkName: "testnet", + NetworkNameFull: "Sui Testnet", + } + + hm, err := NewHealthMetrics(chainInfo) + require.NoError(t, err) + + ctx := context.Background() + + // Record a success for a component + hm.RecordLastSuccess(ctx, ComponentTxM) + + // Wait a bit + time.Sleep(50 * time.Millisecond) + + // Update processing lag - this should not panic and should complete + hm.UpdateProcessingLag(ctx) + + // Verify the component still has a valid last success time + lastSuccess, exists := hm.GetLastSuccessTime(ComponentTxM) + assert.True(t, exists) + assert.False(t, lastSuccess.IsZero()) +} + +func TestConcurrentAccess(t *testing.T) { + t.Parallel() + + chainInfo := config.ChainInfo{ + ChainFamilyName: "sui", + ChainID: "test-chain-id", + NetworkName: "testnet", + NetworkNameFull: "Sui Testnet", + } + + hm, err := NewHealthMetrics(chainInfo) + require.NoError(t, err) + + ctx := context.Background() + done := make(chan struct{}) + + // Start multiple goroutines that access health metrics concurrently + for i := 0; i < 10; i++ { + go func() { + for j := 0; j < 100; j++ { + hm.RecordHealth(ctx, ComponentTxM, j%2 == 0) + hm.RecordLastSuccess(ctx, ComponentEventsIndexer) + hm.UpdateProcessingLag(ctx) + _, _ = hm.GetHealthStatus(ComponentTxM) + _, _ = hm.GetLastSuccessTime(ComponentEventsIndexer) + } + done <- struct{}{} + }() + } + + // Wait for all goroutines to complete + for i := 0; i < 10; i++ { + <-done + } + + // If we got here without panics or deadlocks, the test passes +} + +func TestFlipFlopDetection(t *testing.T) { + t.Parallel() + + chainInfo := config.ChainInfo{ + ChainFamilyName: "sui", + ChainID: "test-chain-id", + NetworkName: "testnet", + NetworkNameFull: "Sui Testnet", + } + + hm, err := NewHealthMetrics(chainInfo) + require.NoError(t, err) + + ctx := context.Background() + component := ComponentSuiRelayer + + // Initial state is unhealthy (false) + // Recording unhealthy again should NOT be a flip-flop + hm.RecordHealth(ctx, component, false) + status, _ := hm.GetHealthStatus(component) + assert.False(t, status) + + // Recording healthy IS a flip-flop + hm.RecordHealth(ctx, component, true) + status, _ = hm.GetHealthStatus(component) + assert.True(t, status) + + // Recording healthy again should NOT be a flip-flop + hm.RecordHealth(ctx, component, true) + status, _ = hm.GetHealthStatus(component) + assert.True(t, status) + + // Recording unhealthy IS a flip-flop + hm.RecordHealth(ctx, component, false) + status, _ = hm.GetHealthStatus(component) + assert.False(t, status) +} diff --git a/relayer/plugin/relayer.go b/relayer/plugin/relayer.go index 0b84ec5cc..637b5dd4a 100644 --- a/relayer/plugin/relayer.go +++ b/relayer/plugin/relayer.go @@ -22,6 +22,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/types/core" aptosBalanceMonitor "github.com/smartcontractkit/chainlink-aptos/relayer/monitor" + chainreaderConfig "github.com/smartcontractkit/chainlink-sui/relayer/chainreader/config" chainreader "github.com/smartcontractkit/chainlink-sui/relayer/chainreader/reader" "github.com/smartcontractkit/chainlink-sui/relayer/chainwriter" @@ -30,6 +31,9 @@ import ( "github.com/smartcontractkit/chainlink-sui/relayer/txm" ) +// HealthMetricsPollInterval defines how often health metrics are published +const HealthMetricsPollInterval = 15 * time.Second + type SuiRelayer struct { types.UnimplementedRelayer services.StateMachine @@ -46,6 +50,11 @@ type SuiRelayer struct { balanceMonitor services.Service indexer *indexer.Indexer + + // Health metrics for monitoring + healthMetrics *monitor.HealthMetrics + healthStopChan chan struct{} + healthDone chan struct{} } var _ types.Relayer = &SuiRelayer{} @@ -188,6 +197,22 @@ func NewRelayer(cfg *config.TOMLConfig, lggr logger.Logger, keystore core.Keysto return nil, fmt.Errorf("error in NewRelayer (monitor) - failed to create new balance monitor: %w", err) } + // Initialize health metrics for monitoring + chainInfo := config.ChainInfo{ + ChainFamilyName: "sui", + ChainID: *cfg.ChainID, + NetworkName: *cfg.NetworkName, + NetworkNameFull: *cfg.NetworkNameFull, + } + healthMetrics, err := monitor.NewHealthMetrics(chainInfo) + if err != nil { + return nil, fmt.Errorf("error in NewRelayer - failed to create health metrics: %w", err) + } + + // Set health metrics on components that support it + txManager.SetHealthMetrics(healthMetrics) + indexerInstance.SetHealthMetrics(healthMetrics) + return &SuiRelayer{ chainId: id, chainIdNum: idNum, @@ -198,6 +223,9 @@ func NewRelayer(cfg *config.TOMLConfig, lggr logger.Logger, keystore core.Keysto balanceMonitor: balanceMonitorService, db: db, indexer: indexerInstance, + healthMetrics: healthMetrics, + healthStopChan: make(chan struct{}), + healthDone: make(chan struct{}), }, nil } @@ -218,7 +246,14 @@ func (r *SuiRelayer) Start(ctx context.Context) error { r.lggr.Debug("Starting Sui Relayer") var ms services.MultiStart - return ms.Start(ctx, r.txm, r.indexer, r.balanceMonitor) + if err := ms.Start(ctx, r.txm, r.indexer, r.balanceMonitor); err != nil { + return err + } + + // Start health metrics publishing goroutine + go r.healthMetricsLoop() + + return nil }) } @@ -226,6 +261,10 @@ func (r *SuiRelayer) Close() error { return r.StopOnce("SuiRelayer", func() error { r.lggr.Debug("Stopping Sui Relayer") + // Stop health metrics goroutine + close(r.healthStopChan) + <-r.healthDone + return services.CloseAll(r.txm, r.indexer, r.balanceMonitor) }) } @@ -242,10 +281,52 @@ func (r *SuiRelayer) Ready() error { func (r *SuiRelayer) HealthReport() map[string]error { report := map[string]error{r.Name(): r.Healthy()} services.CopyHealth(report, r.txm.HealthReport()) + services.CopyHealth(report, r.indexer.HealthReport()) return report } +// healthMetricsLoop periodically publishes health metrics for all components. +func (r *SuiRelayer) healthMetricsLoop() { + defer close(r.healthDone) + + ticker := time.NewTicker(HealthMetricsPollInterval) + defer ticker.Stop() + + r.lggr.Info("Starting health metrics publishing loop") + + for { + select { + case <-r.healthStopChan: + r.lggr.Info("Health metrics loop stopped") + return + case <-ticker.C: + r.publishHealthMetrics() + } + } +} + +// publishHealthMetrics collects and publishes health metrics for all components. +func (r *SuiRelayer) publishHealthMetrics() { + ctx := context.Background() + + // Collect health reports from all components + report := r.HealthReport() + + // Record health status for each component + r.healthMetrics.RecordHealthFromReport(ctx, report) + + // Update processing lag for all components + r.healthMetrics.UpdateProcessingLag(ctx) + + r.lggr.Debugw("Published health metrics", "report", report) +} + +// GetHealthMetrics returns the health metrics instance for external access. +func (r *SuiRelayer) GetHealthMetrics() *monitor.HealthMetrics { + return r.healthMetrics +} + // ChainService interface func (r *SuiRelayer) GetChainStatus(ctx context.Context) (types.ChainStatus, error) { toml, err := r.cfg.TOMLString() diff --git a/relayer/txm/confirmer.go b/relayer/txm/confirmer.go index ff5c327c4..b3701121a 100644 --- a/relayer/txm/confirmer.go +++ b/relayer/txm/confirmer.go @@ -83,7 +83,7 @@ func checkConfirmations(loopCtx context.Context, txm *SuiTxm) { switch resp.Status { case success: - if err := handleSuccess(txm, tx); err != nil { + if err := handleSuccess(loopCtx, txm, tx); err != nil { txm.lggr.Errorw("Error handling successful transaction", "transactionID", tx.TransactionID, "error", err) } case failure: @@ -96,13 +96,16 @@ func checkConfirmations(loopCtx context.Context, txm *SuiTxm) { } } -func handleSuccess(txm *SuiTxm, tx SuiTx) error { +func handleSuccess(ctx context.Context, txm *SuiTxm, tx SuiTx) error { if err := txm.transactionRepository.ChangeState(tx.TransactionID, StateFinalized); err != nil { txm.lggr.Errorw("Failed to update transaction state", "transactionID", tx.TransactionID, "error", err) return err } txm.lggr.Infow("Transaction finalized", "transactionID", tx.TransactionID) + // Record successful transaction in health metrics + txm.recordLastSuccess(ctx) + if err := txm.coinManager.ReleaseCoins(tx.TransactionID); err != nil { // This error is not critical, can be safely ignored as the coins will auto-release after the default TTL txm.lggr.Debugw("Failed to release coins", "transactionID", tx.TransactionID, "error", err) diff --git a/relayer/txm/txm.go b/relayer/txm/txm.go index 28b9caaf1..1535c80f5 100644 --- a/relayer/txm/txm.go +++ b/relayer/txm/txm.go @@ -14,6 +14,7 @@ import ( "github.com/block-vision/sui-go-sdk/transaction" "github.com/smartcontractkit/chainlink-sui/relayer/client" + "github.com/smartcontractkit/chainlink-sui/relayer/monitor" ) const numberGoroutines = 3 @@ -39,6 +40,9 @@ type SuiTxm struct { done sync.WaitGroup broadcastChannel chan string stopChannel chan struct{} + + // Health metrics for monitoring (optional) + healthMetrics *monitor.HealthMetrics } func NewSuiTxm( @@ -193,4 +197,17 @@ func (txm *SuiTxm) GetGasManager() GasManager { return txm.gasManager } +// SetHealthMetrics sets the health metrics instance for the transaction manager. +// This should be called after creating the TxM to enable health metrics reporting. +func (txm *SuiTxm) SetHealthMetrics(hm *monitor.HealthMetrics) { + txm.healthMetrics = hm +} + +// recordLastSuccess records a successful operation to the health metrics. +func (txm *SuiTxm) recordLastSuccess(ctx context.Context) { + if txm.healthMetrics != nil { + txm.healthMetrics.RecordLastSuccess(ctx, monitor.ComponentTxM) + } +} + var _ TxManager = (*SuiTxm)(nil) From dd7c1c3d8794f33556fcc8730c657d072894579d Mon Sep 17 00:00:00 2001 From: faisal-link Date: Mon, 19 Jan 2026 16:56:43 +0400 Subject: [PATCH 2/5] record health metric success in CW --- relayer/chainwriter/chainwriter.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/relayer/chainwriter/chainwriter.go b/relayer/chainwriter/chainwriter.go index e0d430a12..667911a3d 100644 --- a/relayer/chainwriter/chainwriter.go +++ b/relayer/chainwriter/chainwriter.go @@ -144,6 +144,8 @@ func (s *SuiChainWriter) SubmitTransaction(ctx context.Context, contractName str } s.lggr.Infow("Transaction enqueued", "transactionID", tx.TransactionID, "functionName", method) + s.recordLastSuccess(ctx) + return nil } From 0ff319c2946c9189059d7a29e5ef7ca1d1ac848a Mon Sep 17 00:00:00 2001 From: faisal-link Date: Mon, 19 Jan 2026 18:58:56 +0400 Subject: [PATCH 3/5] re-use the ChainConfig from aptos --- relayer/config/chain_config.go | 7 ------- relayer/monitor/health_metrics.go | 6 +++--- relayer/monitor/health_metrics_test.go | 16 ++++++++-------- relayer/monitor/metrics.go | 6 +++--- relayer/plugin/relayer.go | 2 +- 5 files changed, 15 insertions(+), 22 deletions(-) diff --git a/relayer/config/chain_config.go b/relayer/config/chain_config.go index f86bc9644..04b9cb364 100644 --- a/relayer/config/chain_config.go +++ b/relayer/config/chain_config.go @@ -23,13 +23,6 @@ const ( DefaultTransactionRetentionSecs = uint64(10) ) -type ChainInfo struct { - ChainFamilyName string - ChainID string - NetworkName string - NetworkNameFull string -} - type NodeConfig struct { Name *string URL *config.URL diff --git a/relayer/monitor/health_metrics.go b/relayer/monitor/health_metrics.go index cfe040e53..f6e0c8484 100644 --- a/relayer/monitor/health_metrics.go +++ b/relayer/monitor/health_metrics.go @@ -6,7 +6,7 @@ import ( "sync" "time" - "github.com/smartcontractkit/chainlink-sui/relayer/config" + aptosMonitor "github.com/smartcontractkit/chainlink-aptos/relayer/monitor" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric" @@ -30,7 +30,7 @@ const ( // It tracks component status, flip-flop counts, and processing lag for monitoring // and alerting purposes. type HealthMetrics struct { - chainInfo config.ChainInfo + chainInfo aptosMonitor.ChainInfo // Metrics statusGauge metric.Int64Gauge @@ -46,7 +46,7 @@ type HealthMetrics struct { // NewHealthMetrics creates a new HealthMetrics instance and pre-registers all metrics. // Metrics are registered at startup so they are always present for alerting. -func NewHealthMetrics(chainInfo config.ChainInfo) (*HealthMetrics, error) { +func NewHealthMetrics(chainInfo aptosMonitor.ChainInfo) (*HealthMetrics, error) { meter := beholder.GetMeter() statusGauge, err := meter.Int64Gauge( diff --git a/relayer/monitor/health_metrics_test.go b/relayer/monitor/health_metrics_test.go index 36aa620f9..27be1896b 100644 --- a/relayer/monitor/health_metrics_test.go +++ b/relayer/monitor/health_metrics_test.go @@ -8,13 +8,13 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink-sui/relayer/config" + aptosMonitor "github.com/smartcontractkit/chainlink-aptos/relayer/monitor" ) func TestNewHealthMetrics(t *testing.T) { t.Parallel() - chainInfo := config.ChainInfo{ + chainInfo := aptosMonitor.ChainInfo{ ChainFamilyName: "sui", ChainID: "test-chain-id", NetworkName: "testnet", @@ -51,7 +51,7 @@ func TestNewHealthMetrics(t *testing.T) { func TestRecordHealth(t *testing.T) { t.Parallel() - chainInfo := config.ChainInfo{ + chainInfo := aptosMonitor.ChainInfo{ ChainFamilyName: "sui", ChainID: "test-chain-id", NetworkName: "testnet", @@ -91,7 +91,7 @@ func TestRecordHealth(t *testing.T) { func TestRecordLastSuccess(t *testing.T) { t.Parallel() - chainInfo := config.ChainInfo{ + chainInfo := aptosMonitor.ChainInfo{ ChainFamilyName: "sui", ChainID: "test-chain-id", NetworkName: "testnet", @@ -123,7 +123,7 @@ func TestRecordLastSuccess(t *testing.T) { func TestRecordHealthFromReport(t *testing.T) { t.Parallel() - chainInfo := config.ChainInfo{ + chainInfo := aptosMonitor.ChainInfo{ ChainFamilyName: "sui", ChainID: "test-chain-id", NetworkName: "testnet", @@ -166,7 +166,7 @@ func TestRecordHealthFromReport(t *testing.T) { func TestUpdateProcessingLag(t *testing.T) { t.Parallel() - chainInfo := config.ChainInfo{ + chainInfo := aptosMonitor.ChainInfo{ ChainFamilyName: "sui", ChainID: "test-chain-id", NetworkName: "testnet", @@ -196,7 +196,7 @@ func TestUpdateProcessingLag(t *testing.T) { func TestConcurrentAccess(t *testing.T) { t.Parallel() - chainInfo := config.ChainInfo{ + chainInfo := aptosMonitor.ChainInfo{ ChainFamilyName: "sui", ChainID: "test-chain-id", NetworkName: "testnet", @@ -234,7 +234,7 @@ func TestConcurrentAccess(t *testing.T) { func TestFlipFlopDetection(t *testing.T) { t.Parallel() - chainInfo := config.ChainInfo{ + chainInfo := aptosMonitor.ChainInfo{ ChainFamilyName: "sui", ChainID: "test-chain-id", NetworkName: "testnet", diff --git a/relayer/monitor/metrics.go b/relayer/monitor/metrics.go index 5d10c9f40..579246195 100644 --- a/relayer/monitor/metrics.go +++ b/relayer/monitor/metrics.go @@ -4,7 +4,7 @@ import ( "context" "fmt" - "github.com/smartcontractkit/chainlink-sui/relayer/config" + aptosMonitor "github.com/smartcontractkit/chainlink-aptos/relayer/monitor" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric" @@ -31,12 +31,12 @@ func NewGaugeAccBalance(unitStr string) (*GaugeAccBalance, error) { return &GaugeAccBalance{gauge}, nil } -func (g *GaugeAccBalance) Record(ctx context.Context, balance float64, account string, chainInfo config.ChainInfo) { +func (g *GaugeAccBalance) Record(ctx context.Context, balance float64, account string, chainInfo aptosMonitor.ChainInfo) { oAttrs := metric.WithAttributeSet(g.GetAttributes(account, chainInfo)) g.gauge.Record(ctx, balance, oAttrs) } -func (g *GaugeAccBalance) GetAttributes(account string, chainInfo config.ChainInfo) attribute.Set { +func (g *GaugeAccBalance) GetAttributes(account string, chainInfo aptosMonitor.ChainInfo) attribute.Set { return attribute.NewSet( attribute.String("account", account), diff --git a/relayer/plugin/relayer.go b/relayer/plugin/relayer.go index 637b5dd4a..04839b8e2 100644 --- a/relayer/plugin/relayer.go +++ b/relayer/plugin/relayer.go @@ -198,7 +198,7 @@ func NewRelayer(cfg *config.TOMLConfig, lggr logger.Logger, keystore core.Keysto } // Initialize health metrics for monitoring - chainInfo := config.ChainInfo{ + chainInfo := aptosBalanceMonitor.ChainInfo{ ChainFamilyName: "sui", ChainID: *cfg.ChainID, NetworkName: *cfg.NetworkName, From 4b585768beb43169169131c4f19d339c148c2e78 Mon Sep 17 00:00:00 2001 From: faisal-link Date: Mon, 19 Jan 2026 19:17:59 +0400 Subject: [PATCH 4/5] fix build errors --- go.mod | 25 ++++++----- go.sum | 62 +++++++++++++------------- relayer/monitor/balance.go | 3 +- relayer/monitor/health_metrics.go | 6 +-- relayer/monitor/health_metrics_test.go | 16 +++---- relayer/monitor/metrics.go | 6 +-- relayer/plugin/relayer.go | 5 ++- 7 files changed, 64 insertions(+), 59 deletions(-) diff --git a/go.mod b/go.mod index 0d52daa6e..051ca4c27 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ toolchain go1.24.7 replace github.com/fbsobreira/gotron-sdk => github.com/smartcontractkit/chainlink-tron/relayer/gotron-sdk v0.0.4 require ( - github.com/aptos-labs/aptos-go-sdk v1.7.1-0.20250602153733-bb1facae1d43 + github.com/aptos-labs/aptos-go-sdk v1.11.0 github.com/aptos-labs/tree-sitter-move-on-aptos v0.0.0-20250321090037-c820eb4716e1 github.com/block-vision/sui-go-sdk v1.1.2 github.com/ethereum/go-ethereum v1.16.2 @@ -21,17 +21,17 @@ require ( github.com/pkg/errors v0.9.1 github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 github.com/smacker/go-tree-sitter v0.0.0-20240827094217-dd81d9e9be82 - github.com/smartcontractkit/chainlink-aptos v0.0.0-20250905094443-ac02b032b32b + github.com/smartcontractkit/chainlink-aptos v0.0.0-20251024142440-51f2ad2652a2 github.com/smartcontractkit/chainlink-ccip v0.0.0-20250805210128-7f8a0f403c3a github.com/smartcontractkit/chainlink-common v0.9.1-0.20250815142532-64e0a7965958 - github.com/stretchr/testify v1.10.0 + github.com/stretchr/testify v1.11.1 github.com/test-go/testify v1.1.4 go.opentelemetry.io/otel v1.37.0 go.opentelemetry.io/otel/metric v1.37.0 go.uber.org/mock v0.5.2 - golang.org/x/crypto v0.42.0 + golang.org/x/crypto v0.43.0 golang.org/x/exp v0.0.0-20250711185948-6ae5c78190dc - golang.org/x/net v0.43.0 + golang.org/x/net v0.45.0 golang.org/x/sync v0.17.0 gopkg.in/yaml.v3 v3.0.1 ) @@ -49,7 +49,7 @@ require ( github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cloudevents/sdk-go/binding/format/protobuf/v2 v2.16.1 // indirect github.com/cloudevents/sdk-go/v2 v2.16.1 // indirect - github.com/coder/websocket v1.8.13 // indirect + github.com/coder/websocket v1.8.14 // indirect github.com/cosmos/go-bip39 v1.0.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect @@ -62,7 +62,7 @@ require ( github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.26.0 // indirect - github.com/go-viper/mapstructure/v2 v2.3.0 // indirect + github.com/go-viper/mapstructure/v2 v2.4.0 // indirect github.com/goccy/go-json v0.10.5 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/google/flatbuffers v25.2.10+incompatible // indirect @@ -73,7 +73,7 @@ require ( github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 // indirect github.com/hashicorp/go-hclog v1.6.3 // indirect github.com/hashicorp/yamux v0.1.2 // indirect - github.com/hasura/go-graphql-client v0.13.1 // indirect + github.com/hasura/go-graphql-client v0.14.5 // indirect github.com/hdevalence/ed25519consensus v0.2.0 // indirect github.com/holiman/uint256 v1.3.2 // indirect github.com/invopop/jsonschema v0.13.0 // indirect @@ -145,11 +145,12 @@ require ( go.opentelemetry.io/proto/otlp v1.5.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect - golang.org/x/mod v0.27.0 // indirect - golang.org/x/sys v0.36.0 // indirect - golang.org/x/text v0.29.0 // indirect + golang.org/x/mod v0.28.0 // indirect + golang.org/x/sys v0.37.0 // indirect + golang.org/x/telemetry v0.0.0-20250908211612-aef8a434d053 // indirect + golang.org/x/text v0.30.0 // indirect golang.org/x/time v0.12.0 // indirect - golang.org/x/tools v0.36.0 // indirect + golang.org/x/tools v0.37.0 // indirect golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect google.golang.org/genproto/googleapis/api v0.0.0-20250528174236-200df99c418a // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a // indirect diff --git a/go.sum b/go.sum index 21f884402..3d585263f 100644 --- a/go.sum +++ b/go.sum @@ -14,8 +14,8 @@ github.com/apache/arrow-go/v18 v18.3.0 h1:Xq4A6dZj9Nu33sqZibzn012LNnewkTUlfKVUFD github.com/apache/arrow-go/v18 v18.3.0/go.mod h1:eEM1DnUTHhgGAjf/ChvOAQbUQ+EPohtDrArffvUjPg8= github.com/apache/thrift v0.21.0 h1:tdPmh/ptjE1IJnhbhrcl2++TauVjy242rkV/UzJChnE= github.com/apache/thrift v0.21.0/go.mod h1:W1H8aR/QRtYNvrPeFXBtobyRkd0/YVhTc6i07XIAgDw= -github.com/aptos-labs/aptos-go-sdk v1.7.1-0.20250602153733-bb1facae1d43 h1:Mn2LI+fa8QzVmXQ/30MT76GYTQIxpSB3lzos0hat4qc= -github.com/aptos-labs/aptos-go-sdk v1.7.1-0.20250602153733-bb1facae1d43/go.mod h1:vYm/yHr6cQpoUBMw/Q93SRR1IhP0mPTBrEGjShwUvXc= +github.com/aptos-labs/aptos-go-sdk v1.11.0 h1:vIL1hpjECUiu7zMl9Wz6VV8ttXsrDqKUj0HxoeaIER4= +github.com/aptos-labs/aptos-go-sdk v1.11.0/go.mod h1:8YvYwRg93UcG6pTStCpZdYiscCtKh51sYfeLgIy/41c= github.com/aptos-labs/tree-sitter-move-on-aptos v0.0.0-20250321090037-c820eb4716e1 h1:KD231JW9jSiu5m0J/w//3qJyGRdvrdabKAF+Fbwvzgo= github.com/aptos-labs/tree-sitter-move-on-aptos v0.0.0-20250321090037-c820eb4716e1/go.mod h1:+WZUlAOW0a0+7CrPgFVwmflo1LHH61uw4WSJtboIk48= github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk= @@ -51,8 +51,8 @@ github.com/cloudevents/sdk-go/v2 v2.16.1/go.mod h1:v/kVOaWjNfbvc6tkhhlkhvLapj8Aa github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= -github.com/coder/websocket v1.8.13 h1:f3QZdXy7uGVz+4uCJy2nTZyM0yTBj8yANEHhqlXZ9FE= -github.com/coder/websocket v1.8.13/go.mod h1:LNVeNrXQZfe5qhS9ALED3uA+l5pPqvwXg3CKoDBB2gs= +github.com/coder/websocket v1.8.14 h1:9L0p0iKiNOibykf283eHkKUHHrpG7f65OE3BhhO7v9g= +github.com/coder/websocket v1.8.14/go.mod h1:NX3SzP+inril6yawo5CQXx8+fk145lPDC6pumgx0mVg= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/cosmos/go-bip39 v1.0.0 h1:pcomnQdrdH22njcAatO0yWojsUnCO3y2tNoV1cb6hHY= @@ -60,8 +60,8 @@ github.com/cosmos/go-bip39 v1.0.0/go.mod h1:RNJv0H/pOIVgxw6KS7QeX2a0Uo0aKUlfhZ4x github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/cucumber/gherkin/go/v26 v26.2.0 h1:EgIjePLWiPeslwIWmNQ3XHcypPsWAHoMCz/YEBKP4GI= github.com/cucumber/gherkin/go/v26 v26.2.0/go.mod h1:t2GAPnB8maCT4lkHL99BDCVNzCh1d7dBhCLt150Nr/0= -github.com/cucumber/godog v0.15.0 h1:51AL8lBXF3f0cyA5CV4TnJFCTHpgiy+1x1Hb3TtZUmo= -github.com/cucumber/godog v0.15.0/go.mod h1:FX3rzIDybWABU4kuIXLZ/qtqEe1Ac5RdXmqvACJOces= +github.com/cucumber/godog v0.15.1 h1:rb/6oHDdvVZKS66hrhpjFQFHjthFSrQBCOI1LwshNTI= +github.com/cucumber/godog v0.15.1/go.mod h1:qju+SQDewOljHuq9NSM66s0xEhogx0q30flfxL4WUk8= github.com/cucumber/messages/go/v21 v21.0.1 h1:wzA0LxwjlWQYZd32VTlAVDTkW6inOFmSM+RuOwHZiMI= github.com/cucumber/messages/go/v21 v21.0.1/go.mod h1:zheH/2HS9JLVFukdrsPWoPdmUtmYQAQPLk7w5vWsk5s= github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -107,8 +107,8 @@ github.com/go-playground/validator/v10 v10.26.0/go.mod h1:I5QpIEbmr8On7W0TktmJAu github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-viper/mapstructure/v2 v2.3.0 h1:27XbWsHIqhbdR5TIC911OfYvgSaW93HM+dX7970Q7jk= -github.com/go-viper/mapstructure/v2 v2.3.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= +github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= +github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= @@ -161,16 +161,16 @@ github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB1 github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-memdb v1.3.4 h1:XSL3NR682X/cVk2IeV0d70N4DZ9ljI885xAEU8IoK3c= -github.com/hashicorp/go-memdb v1.3.4/go.mod h1:uBTr1oQbtuMgd1SSGoR8YV27eT3sBHbYiNm53bMpgSg= +github.com/hashicorp/go-memdb v1.3.5 h1:b3taDMxCBCBVgyRrS1AZVHO14ubMYZB++QpNhBg+Nyo= +github.com/hashicorp/go-memdb v1.3.5/go.mod h1:8IVKKBkVe+fxFgdFOYxzQQNjz+sWCyHCdIC/+5+Vy1Y= github.com/hashicorp/go-plugin v1.6.3 h1:xgHB+ZUSYeuJi96WtxEjzi23uh7YQpznjGh0U0UUrwg= github.com/hashicorp/go-plugin v1.6.3/go.mod h1:MRobyh+Wc/nYy1V4KAXUiYfzxoYhs7V1mlH1Z7iY2h0= github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c= github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/yamux v0.1.2 h1:XtB8kyFOyHXYVFnwT5C3+Bdo8gArse7j2AQ0DA0Uey8= github.com/hashicorp/yamux v0.1.2/go.mod h1:C+zze2n6e/7wshOZep2A70/aQU6QBRWJO/G6FT1wIns= -github.com/hasura/go-graphql-client v0.13.1 h1:kKbjhxhpwz58usVl+Xvgah/TDha5K2akNTRQdsEHN6U= -github.com/hasura/go-graphql-client v0.13.1/go.mod h1:k7FF7h53C+hSNFRG3++DdVZWIuHdCaTbI7siTJ//zGQ= +github.com/hasura/go-graphql-client v0.14.5 h1:M9HxxGLCcDZnxJGYyWXAzDYEpommgjW+sUW3V8EaGms= +github.com/hasura/go-graphql-client v0.14.5/go.mod h1:jfSZtBER3or+88Q9vFhWHiFMPppfYILRyl+0zsgPIIw= github.com/hdevalence/ed25519consensus v0.2.0 h1:37ICyZqdyj0lAZ8P4D1d1id3HqbbG1N3iBb1Tb4rdcU= github.com/hdevalence/ed25519consensus v0.2.0/go.mod h1:w3BHWjwJbFU29IRHL1Iqkw3sus+7FctEyM4RqDxYNzo= github.com/holiman/uint256 v1.3.2 h1:a9EgMPSC1AAaj1SZL5zIQD3WbwTuHrMGOerLjGmM/TA= @@ -358,8 +358,8 @@ github.com/smacker/go-tree-sitter v0.0.0-20240827094217-dd81d9e9be82 h1:6C8qej6f github.com/smacker/go-tree-sitter v0.0.0-20240827094217-dd81d9e9be82/go.mod h1:xe4pgH49k4SsmkQq5OT8abwhWmnzkhpgnXeekbx2efw= github.com/smartcontractkit/chain-selectors v1.0.71 h1:5DY50sdoUuBkA2FAOmxXbdzl43Ooy04wFwQW5gs/Bjo= github.com/smartcontractkit/chain-selectors v1.0.71/go.mod h1:xsKM0aN3YGcQKTPRPDDtPx2l4mlTN1Djmg0VVXV40b8= -github.com/smartcontractkit/chainlink-aptos v0.0.0-20250905094443-ac02b032b32b h1:scWqERf9tWr5ocX/nENI1Kc9FqSl2y87bglwjXbe3XA= -github.com/smartcontractkit/chainlink-aptos v0.0.0-20250905094443-ac02b032b32b/go.mod h1:zNZ5rtLkbqsGCjDWb1y8n7BRk2zgflkzmj2GjnLnj08= +github.com/smartcontractkit/chainlink-aptos v0.0.0-20251024142440-51f2ad2652a2 h1:vGdeMwHO3ow88HvxfhA4DDPYNY0X9jmdux7L83UF/W8= +github.com/smartcontractkit/chainlink-aptos v0.0.0-20251024142440-51f2ad2652a2/go.mod h1:iteU0WORHkArACVh/HoY/1bipV4TcNcJdTmom9uIT0E= github.com/smartcontractkit/chainlink-ccip v0.0.0-20250805210128-7f8a0f403c3a h1:EqR0TWzpsIDhu28xromfL3/dfzx398rhUtucT4XLNGc= github.com/smartcontractkit/chainlink-ccip v0.0.0-20250805210128-7f8a0f403c3a/go.mod h1:4e+IBu7TJVlmL31sTDYyYEbwJXjFDbv0jot7QQmBZ9E= github.com/smartcontractkit/chainlink-common v0.9.1-0.20250815142532-64e0a7965958 h1:pfzvL8n8f4ygUe4pIBjHgM2Tzct6qG8Q/jxJjfHThMY= @@ -374,8 +374,8 @@ github.com/smartcontractkit/grpc-proxy v0.0.0-20240830132753-a7e17fec5ab7 h1:12i github.com/smartcontractkit/grpc-proxy v0.0.0-20240830132753-a7e17fec5ab7/go.mod h1:FX7/bVdoep147QQhsOPkYsPEXhGZjeYx6lBSaSXtZOA= github.com/smartcontractkit/libocr v0.0.0-20250408131511-c90716988ee0 h1:yGD0bRNoIQ9vOILzlYg4AiGKMKpJNqi7eIMpW7QIO9Y= github.com/smartcontractkit/libocr v0.0.0-20250408131511-c90716988ee0/go.mod h1:lzZ0Hq8zK1FfPb7aHuKQKrsWlrsCtBs6gNRNXh59H7Q= -github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= -github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= +github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= @@ -393,8 +393,8 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= -github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/test-go/testify v1.1.4 h1:Tf9lntrKUMHiXQ07qBScBTSA0dhYQlu83hswqelv1iE= github.com/test-go/testify v1.1.4/go.mod h1:rH7cfJo/47vWGdi4GPj16x3/t1xGOj2YxzmNQzk2ghU= github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= @@ -493,8 +493,8 @@ golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.20.0/go.mod h1:Xwo95rrVNIoSMx9wa1JroENMToLWn3RNVrTBpLHgZPQ= -golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI= -golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8= +golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04= +golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20250711185948-6ae5c78190dc h1:TS73t7x3KarrNd5qAipmspBDS1rkMcgVG/fS1aRb4Rc= golang.org/x/exp v0.0.0-20250711185948-6ae5c78190dc/go.mod h1:A+z0yzpGtvnG90cToK5n2tu8UJVP2XUATh+r+sfOOOc= @@ -508,8 +508,8 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ= -golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc= +golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U= +golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -526,8 +526,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= -golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= -golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= +golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM= +golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -567,8 +567,10 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= -golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ= +golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/telemetry v0.0.0-20250908211612-aef8a434d053 h1:dHQOQddU4YHS5gY33/6klKjq7Gp3WwMyOXGNp5nzRj8= +golang.org/x/telemetry v0.0.0-20250908211612-aef8a434d053/go.mod h1:+nZKN+XVh4LCiA9DV3ywrzN4gumyCnKjau3NGb9SGoE= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -585,8 +587,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= -golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= +golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k= +golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM= golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -605,8 +607,8 @@ golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapK golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg= -golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s= +golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE= +golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/relayer/monitor/balance.go b/relayer/monitor/balance.go index 486f2a6be..634ba53d3 100644 --- a/relayer/monitor/balance.go +++ b/relayer/monitor/balance.go @@ -9,6 +9,7 @@ import ( "github.com/smartcontractkit/chainlink-sui/relayer/client" aptosBalanceMonitor "github.com/smartcontractkit/chainlink-aptos/relayer/monitor" + aptosTypes "github.com/smartcontractkit/chainlink-aptos/relayer/types" "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink-common/pkg/types/core" @@ -20,7 +21,7 @@ const BalanceCheckTimeout = 30 * time.Second // BalanceMonitorOpts contains the options for creating a new Sui account balance monitor. type BalanceMonitorOpts struct { - ChainInfo aptosBalanceMonitor.ChainInfo + ChainInfo aptosTypes.ChainInfo Config aptosBalanceMonitor.GenericBalanceConfig Logger logger.Logger diff --git a/relayer/monitor/health_metrics.go b/relayer/monitor/health_metrics.go index f6e0c8484..b7eb227bd 100644 --- a/relayer/monitor/health_metrics.go +++ b/relayer/monitor/health_metrics.go @@ -6,7 +6,7 @@ import ( "sync" "time" - aptosMonitor "github.com/smartcontractkit/chainlink-aptos/relayer/monitor" + aptosTypes "github.com/smartcontractkit/chainlink-aptos/relayer/types" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric" @@ -30,7 +30,7 @@ const ( // It tracks component status, flip-flop counts, and processing lag for monitoring // and alerting purposes. type HealthMetrics struct { - chainInfo aptosMonitor.ChainInfo + chainInfo aptosTypes.ChainInfo // Metrics statusGauge metric.Int64Gauge @@ -46,7 +46,7 @@ type HealthMetrics struct { // NewHealthMetrics creates a new HealthMetrics instance and pre-registers all metrics. // Metrics are registered at startup so they are always present for alerting. -func NewHealthMetrics(chainInfo aptosMonitor.ChainInfo) (*HealthMetrics, error) { +func NewHealthMetrics(chainInfo aptosTypes.ChainInfo) (*HealthMetrics, error) { meter := beholder.GetMeter() statusGauge, err := meter.Int64Gauge( diff --git a/relayer/monitor/health_metrics_test.go b/relayer/monitor/health_metrics_test.go index 27be1896b..f5f1da73c 100644 --- a/relayer/monitor/health_metrics_test.go +++ b/relayer/monitor/health_metrics_test.go @@ -8,13 +8,13 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - aptosMonitor "github.com/smartcontractkit/chainlink-aptos/relayer/monitor" + aptosTypes "github.com/smartcontractkit/chainlink-aptos/relayer/types" ) func TestNewHealthMetrics(t *testing.T) { t.Parallel() - chainInfo := aptosMonitor.ChainInfo{ + chainInfo := aptosTypes.ChainInfo{ ChainFamilyName: "sui", ChainID: "test-chain-id", NetworkName: "testnet", @@ -51,7 +51,7 @@ func TestNewHealthMetrics(t *testing.T) { func TestRecordHealth(t *testing.T) { t.Parallel() - chainInfo := aptosMonitor.ChainInfo{ + chainInfo := aptosTypes.ChainInfo{ ChainFamilyName: "sui", ChainID: "test-chain-id", NetworkName: "testnet", @@ -91,7 +91,7 @@ func TestRecordHealth(t *testing.T) { func TestRecordLastSuccess(t *testing.T) { t.Parallel() - chainInfo := aptosMonitor.ChainInfo{ + chainInfo := aptosTypes.ChainInfo{ ChainFamilyName: "sui", ChainID: "test-chain-id", NetworkName: "testnet", @@ -123,7 +123,7 @@ func TestRecordLastSuccess(t *testing.T) { func TestRecordHealthFromReport(t *testing.T) { t.Parallel() - chainInfo := aptosMonitor.ChainInfo{ + chainInfo := aptosTypes.ChainInfo{ ChainFamilyName: "sui", ChainID: "test-chain-id", NetworkName: "testnet", @@ -166,7 +166,7 @@ func TestRecordHealthFromReport(t *testing.T) { func TestUpdateProcessingLag(t *testing.T) { t.Parallel() - chainInfo := aptosMonitor.ChainInfo{ + chainInfo := aptosTypes.ChainInfo{ ChainFamilyName: "sui", ChainID: "test-chain-id", NetworkName: "testnet", @@ -196,7 +196,7 @@ func TestUpdateProcessingLag(t *testing.T) { func TestConcurrentAccess(t *testing.T) { t.Parallel() - chainInfo := aptosMonitor.ChainInfo{ + chainInfo := aptosTypes.ChainInfo{ ChainFamilyName: "sui", ChainID: "test-chain-id", NetworkName: "testnet", @@ -234,7 +234,7 @@ func TestConcurrentAccess(t *testing.T) { func TestFlipFlopDetection(t *testing.T) { t.Parallel() - chainInfo := aptosMonitor.ChainInfo{ + chainInfo := aptosTypes.ChainInfo{ ChainFamilyName: "sui", ChainID: "test-chain-id", NetworkName: "testnet", diff --git a/relayer/monitor/metrics.go b/relayer/monitor/metrics.go index 579246195..4a4172f6c 100644 --- a/relayer/monitor/metrics.go +++ b/relayer/monitor/metrics.go @@ -4,7 +4,7 @@ import ( "context" "fmt" - aptosMonitor "github.com/smartcontractkit/chainlink-aptos/relayer/monitor" + aptosTypes "github.com/smartcontractkit/chainlink-aptos/relayer/types" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric" @@ -31,12 +31,12 @@ func NewGaugeAccBalance(unitStr string) (*GaugeAccBalance, error) { return &GaugeAccBalance{gauge}, nil } -func (g *GaugeAccBalance) Record(ctx context.Context, balance float64, account string, chainInfo aptosMonitor.ChainInfo) { +func (g *GaugeAccBalance) Record(ctx context.Context, balance float64, account string, chainInfo aptosTypes.ChainInfo) { oAttrs := metric.WithAttributeSet(g.GetAttributes(account, chainInfo)) g.gauge.Record(ctx, balance, oAttrs) } -func (g *GaugeAccBalance) GetAttributes(account string, chainInfo aptosMonitor.ChainInfo) attribute.Set { +func (g *GaugeAccBalance) GetAttributes(account string, chainInfo aptosTypes.ChainInfo) attribute.Set { return attribute.NewSet( attribute.String("account", account), diff --git a/relayer/plugin/relayer.go b/relayer/plugin/relayer.go index 04839b8e2..db6a93d82 100644 --- a/relayer/plugin/relayer.go +++ b/relayer/plugin/relayer.go @@ -22,6 +22,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/types/core" aptosBalanceMonitor "github.com/smartcontractkit/chainlink-aptos/relayer/monitor" + aptosTypes "github.com/smartcontractkit/chainlink-aptos/relayer/types" chainreaderConfig "github.com/smartcontractkit/chainlink-sui/relayer/chainreader/config" chainreader "github.com/smartcontractkit/chainlink-sui/relayer/chainreader/reader" @@ -178,7 +179,7 @@ func NewRelayer(cfg *config.TOMLConfig, lggr logger.Logger, keystore core.Keysto return nil, fmt.Errorf("error in NewRelayer (monitor) - invalid balance poll period: %w", err) } balanceMonitorService, err := monitor.NewBalanceMonitor(monitor.BalanceMonitorOpts{ - ChainInfo: aptosBalanceMonitor.ChainInfo{ + ChainInfo: aptosTypes.ChainInfo{ ChainFamilyName: "sui", ChainID: *cfg.ChainID, NetworkName: *cfg.NetworkName, @@ -198,7 +199,7 @@ func NewRelayer(cfg *config.TOMLConfig, lggr logger.Logger, keystore core.Keysto } // Initialize health metrics for monitoring - chainInfo := aptosBalanceMonitor.ChainInfo{ + chainInfo := aptosTypes.ChainInfo{ ChainFamilyName: "sui", ChainID: *cfg.ChainID, NetworkName: *cfg.NetworkName, From 04897e882831684beaf891ed585aff07676e40fe Mon Sep 17 00:00:00 2001 From: faisal-link Date: Mon, 26 Jan 2026 19:14:12 +0400 Subject: [PATCH 5/5] remove ZeroReceiver E2E test to match core repo --- .github/workflows/sui-ccip-test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/sui-ccip-test.yml b/.github/workflows/sui-ccip-test.yml index c082592b6..f0d9ccfa8 100644 --- a/.github/workflows/sui-ccip-test.yml +++ b/.github/workflows/sui-ccip-test.yml @@ -23,8 +23,8 @@ jobs: sui_version: mainnet-v1.60.1 - test_name: Test_CCIP_Messaging_EVM2Sui sui_version: mainnet-v1.60.1 - - test_name: Test_CCIP_EVM2Sui_ZeroReceiver - sui_version: mainnet-v1.60.1 + # - test_name: Test_CCIP_EVM2Sui_ZeroReceiver + # sui_version: mainnet-v1.60.1 - test_name: Test_CCIPTokenTransfer_Sui2EVM_LockReleaseTokenPool sui_version: mainnet-v1.60.1 - test_name: Test_CCIPTokenTransfer_Sui2EVM_BurnMintTokenPool