From 809838c8644f91c61dab89ba7ed9fe0d3b9413f1 Mon Sep 17 00:00:00 2001 From: ilija42 <57732589+ilija42@users.noreply.github.com> Date: Thu, 22 Oct 2026 12:41:38 +0200 Subject: [PATCH 1/3] stellar: bill the transaction named in the WriteReport reply, as the other chains do After a submit, metering now comes from the reply's on-chain fee instead of the local submit response, so a node whose duplicate failed bills the canonical tx like every other node, and a missing fee yields no record instead of 0. Errors return empty metadata and the wrapper returns nil: the generated capability server drops the output on error, so metering next to an error never reached the DON. Removes meteringFromSubmitResponse. --- .../stellar/actions/write_report.go | 57 +++++++------------ .../stellar/actions/write_report_test.go | 50 +++------------- 2 files changed, 28 insertions(+), 79 deletions(-) diff --git a/chain_capabilities/stellar/actions/write_report.go b/chain_capabilities/stellar/actions/write_report.go index a2b4b7853..3509a8506 100644 --- a/chain_capabilities/stellar/actions/write_report.go +++ b/chain_capabilities/stellar/actions/write_report.go @@ -68,12 +68,6 @@ func (s *Stellar) WriteReport( capErr := capcommon.GetError(err, isUserError) monitoring.LogAndEmitError(ctx, s.lggr, s.beholderProcessor, s.messageBuilder.BuildWriteReportError(telemetryContext, input, "Failed to WriteReport while checking if the report exists or trying to publish on chain", capErr)) - if reply != nil || len(meteringMeta.Metering) > 0 { - return &capabilities.ResponseAndMetadata[*stellarcap.WriteReportReply]{ - Response: reply, - ResponseMetadata: meteringMeta, - }, capErr - } return nil, capErr } @@ -230,8 +224,6 @@ func (wr *writeReport) execute( if err != nil { return nil, capabilities.ResponseMetadata{}, err } - ownMeteringMetadata := wr.meteringFromSubmitResponse(submitResp) - // Poll for the canonical on-chain transmission state. The forwarder may record the // outcome after the tx confirms; retry until it is visible or the context expires. postInfo, pollErr := capcommon.WithPollingRetry(ctx, wr.lggr, func(ctx context.Context) (TransmissionInfo, error) { @@ -252,21 +244,22 @@ func (wr *writeReport) execute( "localTxStatus", submitResp.TxStatus, ) - return nil, ownMeteringMetadata, errors.New("failed to retrieve transmission outcome after report submission") + return nil, capabilities.ResponseMetadata{}, errors.New("failed to retrieve transmission outcome after report submission") } switch postInfo.State { case TransmissionStateSucceeded: if submitResp.TxStatus == stellartypes.TxSuccess && submitResp.TxHash != "" { reply, err := wr.buildSuccessReply(ctx, request, telemetryContext, submitResp.TxHash) - return reply, ownMeteringMetadata, err + if err != nil { + return nil, capabilities.ResponseMetadata{}, err + } + return reply, wr.meteringFromReply(reply), nil } txHash, err := txHashRetriever.GetSuccessfulTransmissionHash(ctx) if err != nil { - // A submit occurred and was paid for; bill the local submit hash even though - // the canonical successful tx hash could not be resolved. - return nil, ownMeteringMetadata, err + return nil, capabilities.ResponseMetadata{}, err } if submitResp.TxStatus != stellartypes.TxSuccess && submitResp.TxHash != "" && submitResp.TxHash != txHash { monitoring.LogAndEmitSuccess(ctx, "Made a new transmission attempt - transmission succeeded, but local submit did not confirm (likely duplicate)", @@ -274,15 +267,18 @@ func (wr *writeReport) execute( wr.messageBuilder.BuildWriteReportDuplicateTx(telemetryContext, request, submitResp.TxHash, txHash)) } reply, err := wr.buildSuccessReply(ctx, request, telemetryContext, txHash) - return reply, ownMeteringMetadata, err + if err != nil { + return nil, capabilities.ResponseMetadata{}, err + } + return reply, wr.meteringFromReply(reply), nil case TransmissionStateFailed, TransmissionStateInvalidReceiver: if submitResp.TxStatus == stellartypes.TxSuccess && submitResp.TxHash != "" { wr.lggr.Errorw("Made a new transmission attempt - transmission failed", "txHash", submitResp.TxHash, "transmissionState", postInfo.State) reply, err := wr.buildRevertReplyFromTx(ctx, request, telemetryContext, submitResp.TxHash, postInfo, transmissionID) if err != nil { - return nil, ownMeteringMetadata, revertReplyBuildError(postInfo, transmissionID, err) + return nil, capabilities.ResponseMetadata{}, revertReplyBuildError(postInfo, transmissionID, err) } - return reply, ownMeteringMetadata, nil + return reply, wr.meteringFromReply(reply), nil } txHash, err := txHashRetriever.GetFailedTransmissionHash(ctx) @@ -292,7 +288,7 @@ func (wr *writeReport) execute( } else { wr.lggr.Errorw("Made a new transmission attempt - transmission failed, unable to retrieve failed transmission tx hash", "error", err, "localTxHash", submitResp.TxHash) } - return nil, ownMeteringMetadata, err + return nil, capabilities.ResponseMetadata{}, err } if submitResp.TxHash != "" && submitResp.TxHash != txHash { monitoring.LogAndEmitSuccess(ctx, "Made a new transmission attempt - transmission failed, but local submit hash differs from canonical failed transmission", @@ -302,13 +298,13 @@ func (wr *writeReport) execute( wr.lggr.Errorw("Made a new transmission attempt - transmission failed", "txHash", txHash, "transmissionState", postInfo.State) reply, err := wr.buildRevertReplyFromTx(ctx, request, telemetryContext, txHash, postInfo, transmissionID) if err != nil { - return nil, ownMeteringMetadata, revertReplyBuildError(postInfo, transmissionID, err) + return nil, capabilities.ResponseMetadata{}, revertReplyBuildError(postInfo, transmissionID, err) } - return reply, ownMeteringMetadata, nil + return reply, wr.meteringFromReply(reply), nil default: wr.lggr.Errorw("Invalid transmission state after submit", "state", postInfo.State, "localTxStatus", submitResp.TxStatus) wr.emitInvalidTransmissionState(ctx, request, telemetryContext, postInfo, transmissionID, "WriteReport invalid transmission state after submit", invalidTransmissionStateError(postInfo.State).Error()) - return nil, ownMeteringMetadata, invalidTransmissionStateError(postInfo.State) + return nil, capabilities.ResponseMetadata{}, invalidTransmissionStateError(postInfo.State) } } @@ -437,9 +433,9 @@ func (wr *writeReport) pollTransmissionInfo( } } -// meteringFromReply returns billing metadata carrying the on-chain transaction fee contained -// in the reply, so that nodes returning early for a prior transmission still report the gas -// spent on chain. An absent fee (unavailable from the chain) yields empty metadata. +// meteringFromReply returns billing metadata carrying the on-chain fee of the transaction the +// reply names, so every node bills the same canonical transaction whether it submitted it, +// returned early for it, or sent a duplicate. An absent fee yields empty metadata. func (wr *writeReport) meteringFromReply(reply *stellarcap.WriteReportReply) capabilities.ResponseMetadata { if reply.TransactionFee == nil { wr.lggr.Warnw("Transaction fee unavailable in reply; skipping metering", "txHash", reply.GetTxHash()) @@ -448,21 +444,6 @@ func (wr *writeReport) meteringFromReply(reply *stellarcap.WriteReportReply) cap return metering.GetResponseMetadataWriteReport(*reply.TransactionFee, wr.chainSelector) } -func (wr *writeReport) meteringFromSubmitResponse(submitResp *stellartypes.SubmitTransactionResponse) capabilities.ResponseMetadata { - if submitResp == nil { - wr.lggr.Errorw("Submitted transaction returned nil response; using zero for metering") - return metering.GetResponseMetadataWriteReport(0, wr.chainSelector) - } - if submitResp.TransactionFee == nil { - wr.lggr.Errorw("Submitted transaction fee is unavailable; using zero for metering", - "txHash", submitResp.TxHash, - "txStatus", submitResp.TxStatus, - ) - return metering.GetResponseMetadataWriteReport(0, wr.chainSelector) - } - return metering.GetResponseMetadataWriteReport(*submitResp.TransactionFee, wr.chainSelector) -} - func (wr *writeReport) checkEstimatedSpendLimit(metadata capabilities.RequestMetadata, estimatedFeeStroops uint64) error { unit := fmt.Sprintf(metering.WriteReportSpendUnitFormat, wr.chainSelector) var limitStr string diff --git a/chain_capabilities/stellar/actions/write_report_test.go b/chain_capabilities/stellar/actions/write_report_test.go index 835f142b6..aa638ad88 100644 --- a/chain_capabilities/stellar/actions/write_report_test.go +++ b/chain_capabilities/stellar/actions/write_report_test.go @@ -636,7 +636,7 @@ func TestWriteReport_EarlyReturn(t *testing.T) { func TestWriteReport_HappyPath(t *testing.T) { t.Parallel() - t.Run("fresh submit succeeds - reply uses own tx and metering uses own submit fee", func(t *testing.T) { + t.Run("fresh submit succeeds - reply and metering use the landed tx", func(t *testing.T) { t.Parallel() h := newWriteReportHelper(t) _, reqMeta, req := newWRReportFixture(t) @@ -665,7 +665,7 @@ func TestWriteReport_HappyPath(t *testing.T) { require.NotNil(t, result.Response.TransactionFee) require.Equal(t, txFee, *result.Response.TransactionFee) requireReplyBlockTimestamp(t, result.Response, testBlockTimestamp) - validateWRMetering(t, result.ResponseMetadata, testWRChainSelector, ownFee) + validateWRMetering(t, result.ResponseMetadata, testWRChainSelector, txFee) h.svc.AssertNotCalled(t, "GetEvents", mock.Anything, mock.Anything) }) } @@ -710,9 +710,7 @@ func TestWriteReport_Submit(t *testing.T) { result, capErr := h.stellar.WriteReport(ctx, reqMeta, req) require.NotNil(t, capErr) require.Contains(t, capErr.Error(), "failed to retrieve transmission outcome after report submission") - require.NotNil(t, result) - require.Nil(t, result.Response) - validateWRMetering(t, result.ResponseMetadata, testWRChainSelector, testFee) + require.Nil(t, result) h.svc.AssertNotCalled(t, "GetEvents", mock.Anything, mock.Anything) }) @@ -797,9 +795,7 @@ func TestWriteReport_Submit(t *testing.T) { result, capErr := h.stellar.WriteReport(ctx, reqMeta, req) require.NotNil(t, capErr) require.Contains(t, capErr.Error(), "failed to retrieve transmission outcome after report submission") - require.NotNil(t, result) - require.Nil(t, result.Response) - validateWRMetering(t, result.ResponseMetadata, testWRChainSelector, 0) + require.Nil(t, result) h.svc.AssertNotCalled(t, "GetEvents", mock.Anything, mock.Anything) }) @@ -834,6 +830,8 @@ func TestWriteReport_Submit(t *testing.T) { require.NotNil(t, result.Response.TxHash) require.Equal(t, testTxHash, *result.Response.TxHash) require.NotEqual(t, "mytx", *result.Response.TxHash) + // The duplicate's own fee is not billed; metering follows the landed tx like every other node. + validateWRMetering(t, result.ResponseMetadata, testWRChainSelector, testFee) requireDuplicateTxTelemetry(t, processor.messages, "mytx", testTxHash) }) @@ -866,6 +864,7 @@ func TestWriteReport_Submit(t *testing.T) { require.NotEqual(t, "mytx", *result.Response.TxHash) require.NotNil(t, result.Response.ErrorMessage) require.Contains(t, *result.Response.ErrorMessage, "not a Wasm contract or missing on_report") + validateWRMetering(t, result.ResponseMetadata, testWRChainSelector, testFee) requireDuplicateTxTelemetry(t, processor.messages, "mytx", testTxHash) }) } @@ -1313,9 +1312,7 @@ func TestWriteReport_TxFatalSubmitWithoutCanonicalOutcomeReturnsError(t *testing result, capErr := h.stellar.WriteReport(ctx, reqMeta, req) require.NotNil(t, capErr) require.Contains(t, capErr.Error(), "failed to retrieve transmission outcome after report submission") - require.NotNil(t, result) - require.Nil(t, result.Response) - validateWRMetering(t, result.ResponseMetadata, testWRChainSelector, 0) + require.Nil(t, result) h.svc.AssertNotCalled(t, "GetEvents", mock.Anything, mock.Anything) } @@ -1408,9 +1405,7 @@ func TestWriteReport_PostSubmitPollFailureDoesNotRecoverFromEvents(t *testing.T) result, capErr := h.stellar.WriteReport(ctx, reqMeta, req) require.NotNil(t, capErr) require.Contains(t, capErr.Error(), "failed to retrieve transmission outcome after report submission") - require.NotNil(t, result) - require.Nil(t, result.Response) - validateWRMetering(t, result.ResponseMetadata, testWRChainSelector, testFee) + require.Nil(t, result) h.svc.AssertNotCalled(t, "GetEvents", mock.Anything, mock.Anything) } @@ -1937,30 +1932,3 @@ func TestCheckEstimatedSpendLimit(t *testing.T) { require.Contains(t, err.Error(), "invalid spend limit") }) } - -func TestMeteringFromSubmitResponse(t *testing.T) { - t.Parallel() - - t.Run("submitted fee bills own fee", func(t *testing.T) { - t.Parallel() - wr := &writeReport{ - lggr: logger.Sugared(logger.Test(t)), - chainSelector: testWRChainSelector, - } - meta := wr.meteringFromSubmitResponse(successSubmitRespWithFee(7_777)) - validateWRMetering(t, meta, testWRChainSelector, 7_777) - }) - - t.Run("nil fee bills explicit zero", func(t *testing.T) { - t.Parallel() - wr := &writeReport{ - lggr: logger.Sugared(logger.Test(t)), - chainSelector: testWRChainSelector, - } - meta := wr.meteringFromSubmitResponse(&stellartypes.SubmitTransactionResponse{ - TxStatus: stellartypes.TxSuccess, - TxHash: testTxHash, - }) - validateWRMetering(t, meta, testWRChainSelector, 0) - }) -} From 014c0d7bdc1953e2cf63b0d92fa6583825c12c90 Mon Sep 17 00:00:00 2001 From: ilija42 <57732589+ilija42@users.noreply.github.com> Date: Thu, 22 Oct 2026 16:18:06 +0200 Subject: [PATCH 2/3] stellar: emit cre:workflow:gas usage records for write reports Wire the shared gasmeter into the Stellar LOOP like EVM and Solana: read the host [Metering] config, build the meter with the chain selector and capability DON id, and emit one usage record per write from meteringFromReply so the record names the same canonical transaction and fee the DON bills. Bumps chain_capabilities/common and chainlink-common to the versions that carry gasmeter. --- chain_capabilities/stellar/actions/actions.go | 8 +++++ .../stellar/actions/write_report.go | 23 +++++++----- .../stellar/actions/write_report_test.go | 36 +++++++++++++++++++ chain_capabilities/stellar/go.mod | 6 ++-- chain_capabilities/stellar/go.sum | 12 +++---- chain_capabilities/stellar/main.go | 20 ++++++++++- 6 files changed, 86 insertions(+), 19 deletions(-) diff --git a/chain_capabilities/stellar/actions/actions.go b/chain_capabilities/stellar/actions/actions.go index f0b3c9e6c..11c441105 100644 --- a/chain_capabilities/stellar/actions/actions.go +++ b/chain_capabilities/stellar/actions/actions.go @@ -24,6 +24,7 @@ import ( "github.com/stellar/go-stellar-sdk/xdr" capcommon "github.com/smartcontractkit/capabilities/chain_capabilities/common" + "github.com/smartcontractkit/capabilities/chain_capabilities/common/gasmeter" commonmon "github.com/smartcontractkit/capabilities/chain_capabilities/common/monitoring" ts "github.com/smartcontractkit/capabilities/chain_capabilities/common/transmission_schedule" "github.com/smartcontractkit/capabilities/libs/chainconsensus" @@ -45,6 +46,7 @@ type Stellar struct { reportSizeLimit limits.BoundLimiter[commoncfg.Size] maxResourceFeeLimit limits.BoundLimiter[uint64] transmissionScheduler ts.TransmissionScheduler + gasMeter *gasmeter.Meter } func NewStellar( @@ -83,6 +85,12 @@ func NewStellar( return st, st.initLimiters(limitsFactory) } +// WithGasMeter enables cre:workflow:gas usage MeterRecords for write reports. +// A nil meter disables emission. +func (s *Stellar) WithGasMeter(m *gasmeter.Meter) { + s.gasMeter = m +} + func (s *Stellar) initLimiters(limitsFactory limits.Factory) (err error) { s.reportSizeLimit, err = limits.MakeUpperBoundLimiter(limitsFactory, cresettings.Default.PerWorkflow.ChainWrite.ReportSizeLimit) if err != nil { diff --git a/chain_capabilities/stellar/actions/write_report.go b/chain_capabilities/stellar/actions/write_report.go index 3509a8506..24e137061 100644 --- a/chain_capabilities/stellar/actions/write_report.go +++ b/chain_capabilities/stellar/actions/write_report.go @@ -22,6 +22,7 @@ import ( stellartypes "github.com/smartcontractkit/chainlink-common/pkg/types/chains/stellar" capcommon "github.com/smartcontractkit/capabilities/chain_capabilities/common" + "github.com/smartcontractkit/capabilities/chain_capabilities/common/gasmeter" ts "github.com/smartcontractkit/capabilities/chain_capabilities/common/transmission_schedule" "github.com/smartcontractkit/capabilities/chain_capabilities/stellar/metering" @@ -43,6 +44,7 @@ type writeReport struct { transmissionScheduler ts.TransmissionScheduler messageBuilder *monitoring.MessageBuilder beholderProcessor beholder.ProtoProcessor + gasMeter *gasmeter.Meter } func (s *Stellar) WriteReport( @@ -96,6 +98,7 @@ func (s *Stellar) executeWriteReport( transmissionScheduler: s.transmissionScheduler, messageBuilder: s.messageBuilder, beholderProcessor: s.beholderProcessor, + gasMeter: s.gasMeter, } return wr.execute(ctx, request, metadata, telemetryContext) } @@ -150,7 +153,7 @@ func (wr *writeReport) execute( if err != nil { return nil, capabilities.ResponseMetadata{}, err } - return reply, wr.meteringFromReply(reply), nil + return reply, wr.meteringFromReply(ctx, metadata, reply), nil case TransmissionStateInvalidReceiver: txHash, hashErr := txHashRetriever.GetFailedTransmissionHash(ctx) if hashErr != nil { @@ -165,7 +168,7 @@ func (wr *writeReport) execute( if err != nil { return nil, capabilities.ResponseMetadata{}, revertReplyBuildError(info, transmissionID, err) } - return reply, wr.meteringFromReply(reply), nil + return reply, wr.meteringFromReply(ctx, metadata, reply), nil case TransmissionStateFailed: txHash, hashErr := txHashRetriever.GetFailedTransmissionHash(ctx) if hashErr != nil { @@ -180,7 +183,7 @@ func (wr *writeReport) execute( if err != nil { return nil, capabilities.ResponseMetadata{}, revertReplyBuildError(info, transmissionID, err) } - return reply, wr.meteringFromReply(reply), nil + return reply, wr.meteringFromReply(ctx, metadata, reply), nil case TransmissionStateNotAttempted: case TransmissionStateUnknown: // Unknown state must not authorize spend. @@ -254,7 +257,7 @@ func (wr *writeReport) execute( if err != nil { return nil, capabilities.ResponseMetadata{}, err } - return reply, wr.meteringFromReply(reply), nil + return reply, wr.meteringFromReply(ctx, metadata, reply), nil } txHash, err := txHashRetriever.GetSuccessfulTransmissionHash(ctx) @@ -270,7 +273,7 @@ func (wr *writeReport) execute( if err != nil { return nil, capabilities.ResponseMetadata{}, err } - return reply, wr.meteringFromReply(reply), nil + return reply, wr.meteringFromReply(ctx, metadata, reply), nil case TransmissionStateFailed, TransmissionStateInvalidReceiver: if submitResp.TxStatus == stellartypes.TxSuccess && submitResp.TxHash != "" { wr.lggr.Errorw("Made a new transmission attempt - transmission failed", "txHash", submitResp.TxHash, "transmissionState", postInfo.State) @@ -278,7 +281,7 @@ func (wr *writeReport) execute( if err != nil { return nil, capabilities.ResponseMetadata{}, revertReplyBuildError(postInfo, transmissionID, err) } - return reply, wr.meteringFromReply(reply), nil + return reply, wr.meteringFromReply(ctx, metadata, reply), nil } txHash, err := txHashRetriever.GetFailedTransmissionHash(ctx) @@ -300,7 +303,7 @@ func (wr *writeReport) execute( if err != nil { return nil, capabilities.ResponseMetadata{}, revertReplyBuildError(postInfo, transmissionID, err) } - return reply, wr.meteringFromReply(reply), nil + return reply, wr.meteringFromReply(ctx, metadata, reply), nil default: wr.lggr.Errorw("Invalid transmission state after submit", "state", postInfo.State, "localTxStatus", submitResp.TxStatus) wr.emitInvalidTransmissionState(ctx, request, telemetryContext, postInfo, transmissionID, "WriteReport invalid transmission state after submit", invalidTransmissionStateError(postInfo.State).Error()) @@ -435,12 +438,14 @@ func (wr *writeReport) pollTransmissionInfo( // meteringFromReply returns billing metadata carrying the on-chain fee of the transaction the // reply names, so every node bills the same canonical transaction whether it submitted it, -// returned early for it, or sent a duplicate. An absent fee yields empty metadata. -func (wr *writeReport) meteringFromReply(reply *stellarcap.WriteReportReply) capabilities.ResponseMetadata { +// returned early for it, or sent a duplicate. It also emits the cre:workflow:gas usage record +// for that transaction. An absent fee yields empty metadata and no record. +func (wr *writeReport) meteringFromReply(ctx context.Context, metadata capabilities.RequestMetadata, reply *stellarcap.WriteReportReply) capabilities.ResponseMetadata { if reply.TransactionFee == nil { wr.lggr.Warnw("Transaction fee unavailable in reply; skipping metering", "txHash", reply.GetTxHash()) return capabilities.ResponseMetadata{} } + wr.gasMeter.Emit(ctx, metadata, reply.GetTxHash(), new(big.Int).SetUint64(*reply.TransactionFee)) return metering.GetResponseMetadataWriteReport(*reply.TransactionFee, wr.chainSelector) } diff --git a/chain_capabilities/stellar/actions/write_report_test.go b/chain_capabilities/stellar/actions/write_report_test.go index aa638ad88..618be4a9a 100644 --- a/chain_capabilities/stellar/actions/write_report_test.go +++ b/chain_capabilities/stellar/actions/write_report_test.go @@ -19,11 +19,13 @@ import ( p2ptypes "github.com/smartcontractkit/libocr/ragep2p/types" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" + "go.uber.org/zap/zapcore" "github.com/smartcontractkit/chainlink-common/pkg/capabilities" ocrtypes "github.com/smartcontractkit/chainlink-common/pkg/capabilities/consensus/ocr3/types" stellarcap "github.com/smartcontractkit/chainlink-common/pkg/capabilities/v2/chain-capabilities/stellar" "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink-common/pkg/resourcemanager" "github.com/smartcontractkit/chainlink-common/pkg/settings/limits" "github.com/smartcontractkit/chainlink-common/pkg/types" stellartypes "github.com/smartcontractkit/chainlink-common/pkg/types/chains/stellar" @@ -31,6 +33,7 @@ import ( workflowpb "github.com/smartcontractkit/chainlink-protos/cre/go/sdk" capcommon "github.com/smartcontractkit/capabilities/chain_capabilities/common" + "github.com/smartcontractkit/capabilities/chain_capabilities/common/gasmeter" commontest "github.com/smartcontractkit/capabilities/chain_capabilities/common/test" ts "github.com/smartcontractkit/capabilities/chain_capabilities/common/transmission_schedule" @@ -1932,3 +1935,36 @@ func TestCheckEstimatedSpendLimit(t *testing.T) { require.Contains(t, err.Error(), "invalid spend limit") }) } + +type discardEmitter struct{} + +func (discardEmitter) Emit(context.Context, []byte, ...any) error { return nil } + +func TestMeteringFromReply_EmitsGasUsageRecord(t *testing.T) { + t.Parallel() + lggr, obs := logger.TestObserved(t, zapcore.InfoLevel) + meter := gasmeter.New(lggr, resourcemanager.Config{ + MeterRecordsEnabled: true, + Emitter: discardEmitter{}, + DeploymentIdentity: resourcemanager.DeploymentIdentity{Product: "cre", Tenant: "t", Environment: "test", Zone: "z", NodeID: "node-1"}, + }, testWRChainSelector, 7) + require.NotNil(t, meter) + wr := &writeReport{lggr: logger.Sugared(lggr), chainSelector: testWRChainSelector, gasMeter: meter} + metadata := capabilities.RequestMetadata{WorkflowID: "wf-1", WorkflowExecutionID: "exec-1", OrgID: "org-1"} + + meta := wr.meteringFromReply(t.Context(), metadata, &stellarcap.WriteReportReply{TxHash: new(testTxHash), TransactionFee: new(testFee)}) + + validateWRMetering(t, meta, testWRChainSelector, testFee) + logs := obs.FilterMessage("Emitted capability usage meter record").All() + require.Len(t, logs, 1) + fields := logs[0].ContextMap() + require.Equal(t, "exec-1", fields["executionID"]) + require.Equal(t, testTxHash, fields["txHash"]) + require.Equal(t, fmt.Sprintf("cre:workflow:gas:%d", testWRChainSelector), fields["resourceType"]) + require.Equal(t, strconv.FormatUint(testFee, 10), fields["value"]) + + // Without a meter the metering metadata is unchanged and nothing is emitted. + meta = (&writeReport{lggr: logger.Sugared(lggr), chainSelector: testWRChainSelector}).meteringFromReply(t.Context(), metadata, &stellarcap.WriteReportReply{TxHash: new(testTxHash), TransactionFee: new(testFee)}) + validateWRMetering(t, meta, testWRChainSelector, testFee) + require.Len(t, obs.FilterMessage("Emitted capability usage meter record").All(), 1) +} diff --git a/chain_capabilities/stellar/go.mod b/chain_capabilities/stellar/go.mod index d28f55425..bdef2ad95 100644 --- a/chain_capabilities/stellar/go.mod +++ b/chain_capabilities/stellar/go.mod @@ -3,10 +3,10 @@ module github.com/smartcontractkit/capabilities/chain_capabilities/stellar go 1.27.3 require ( - github.com/smartcontractkit/capabilities/chain_capabilities/common v0.0.0-20260922114021-5cee95428866 + github.com/smartcontractkit/capabilities/chain_capabilities/common v0.0.0-20261009182120-372f846f528c github.com/smartcontractkit/capabilities/libs v0.0.0-20261001142216-b1dea8dbb961 github.com/smartcontractkit/chain-selectors v1.0.104 - github.com/smartcontractkit/chainlink-common v0.11.2-0.20260925190414-e1b87ad2d7b1 + github.com/smartcontractkit/chainlink-common v0.11.2-0.20261006175240-29594528f464 github.com/smartcontractkit/chainlink-framework/multinode v0.0.0-20260521164805-26d78d5e1243 github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260821155228-fa1d775e2138 github.com/smartcontractkit/libocr v0.0.0-20260810200708-618b5bf7f342 @@ -80,7 +80,7 @@ require ( github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 // indirect github.com/scylladb/go-reflectx v1.0.1 // indirect github.com/shopspring/decimal v1.4.0 // indirect - github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.20260915184316-2730f1867c92 // indirect + github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.20261005132539-b7d63d5fd549 // indirect github.com/smartcontractkit/chainlink-framework/metrics v0.0.0-20260401162955-be2bc6b5264b // indirect github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20251002192024-d2ad9222409b // indirect github.com/smartcontractkit/chainlink-protos/metering/go v0.0.0-20260710151514-27b5a126dabe // indirect diff --git a/chain_capabilities/stellar/go.sum b/chain_capabilities/stellar/go.sum index 27fe98531..25dba54f6 100644 --- a/chain_capabilities/stellar/go.sum +++ b/chain_capabilities/stellar/go.sum @@ -206,16 +206,16 @@ github.com/scylladb/go-reflectx v1.0.1 h1:b917wZM7189pZdlND9PbIJ6NQxfDPfBvUaQ7cj github.com/scylladb/go-reflectx v1.0.1/go.mod h1:rWnOfDIRWBGN0miMLIcoPt/Dhi2doCMZqwMCJ3KupFc= github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= -github.com/smartcontractkit/capabilities/chain_capabilities/common v0.0.0-20260922114021-5cee95428866 h1:nJ3V1j6QSFJ3zfwTO8Ty5TVNEb7g4HUkr9VXXV7hLSg= -github.com/smartcontractkit/capabilities/chain_capabilities/common v0.0.0-20260922114021-5cee95428866/go.mod h1:URIXl+ew48QapONMSLC1faCcGRnm5CRL3jj1PtpJt5E= +github.com/smartcontractkit/capabilities/chain_capabilities/common v0.0.0-20261009182120-372f846f528c h1:R8cKOczBWJ31Spy7ii/+Im269YeWbk6oJ33vDd6cJ3Q= +github.com/smartcontractkit/capabilities/chain_capabilities/common v0.0.0-20261009182120-372f846f528c/go.mod h1:ubztvmJnKJilCw4CzrlHSkKvPYXNeUArML/xY1VzsBg= github.com/smartcontractkit/capabilities/libs v0.0.0-20261001142216-b1dea8dbb961 h1:2+0wOWEiYcEm0XlS1HwgH09y/3qZ0zjokTdbExY2VQQ= github.com/smartcontractkit/capabilities/libs v0.0.0-20261001142216-b1dea8dbb961/go.mod h1:h4kazMLsDM6CsKl2OnT9AtCHhhRsFptA/OTM7Hkj2GE= github.com/smartcontractkit/chain-selectors v1.0.104 h1:/n9pPGM5W/+r1eHoWZv4VwX9LNS1af4+ICyhM8zKRNM= github.com/smartcontractkit/chain-selectors v1.0.104/go.mod h1:qy7whtgG5g+7z0jt0nRyii9bLND9m15NZTzuQPkMZ5w= -github.com/smartcontractkit/chainlink-common v0.11.2-0.20260925190414-e1b87ad2d7b1 h1:1rGIv4vUt+Et7k5waRbkODOQsMuEakQ8TA9PiMBaPss= -github.com/smartcontractkit/chainlink-common v0.11.2-0.20260925190414-e1b87ad2d7b1/go.mod h1:UkZJ6uUU9Hlonfb+k62Pg4yRuvOsoShoOd4tPEB/qpE= -github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.20260915184316-2730f1867c92 h1:Z1OIGukDhJD+Gknr5G07Vtml24E7BwKaIQoWzV/SdqA= -github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.20260915184316-2730f1867c92/go.mod h1:Scyk7O80fde5rJF3Ty3fEE3xiS6j84eWbu8/2gl3pIY= +github.com/smartcontractkit/chainlink-common v0.11.2-0.20261006175240-29594528f464 h1:DVXwyIGekGqLaFAcSWa3K3S209gAY0WDFwliYdLp1dk= +github.com/smartcontractkit/chainlink-common v0.11.2-0.20261006175240-29594528f464/go.mod h1:37Mu0pbAnGmHPgOZ8bQvgP4hzXw/Wu7smKX4cN46UOs= +github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.20261005132539-b7d63d5fd549 h1:J4vYRJxkBO8Yf//17wScKjYWmS45VZaFWx0zt+4ETEU= +github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.20261005132539-b7d63d5fd549/go.mod h1:Scyk7O80fde5rJF3Ty3fEE3xiS6j84eWbu8/2gl3pIY= github.com/smartcontractkit/chainlink-framework/metrics v0.0.0-20260401162955-be2bc6b5264b h1:L1So1EDBDRET3j/TdV1Gjv3qWARoa/NPRaU7k4r30yA= github.com/smartcontractkit/chainlink-framework/metrics v0.0.0-20260401162955-be2bc6b5264b/go.mod h1:HG/aei0MgBOpsyRLexdKGtOUO8yjSJO3iUu0Uu8KBm4= github.com/smartcontractkit/chainlink-framework/multinode v0.0.0-20260521164805-26d78d5e1243 h1:71PGTkjdFZ0JrloEC2Fs8eHl1b1gmUuH+bq7q23usKk= diff --git a/chain_capabilities/stellar/main.go b/chain_capabilities/stellar/main.go index 84e95299c..825a0264d 100644 --- a/chain_capabilities/stellar/main.go +++ b/chain_capabilities/stellar/main.go @@ -18,6 +18,7 @@ import ( "github.com/smartcontractkit/capabilities/libs/loopserver" capcommon "github.com/smartcontractkit/capabilities/chain_capabilities/common" + "github.com/smartcontractkit/capabilities/chain_capabilities/common/gasmeter" ts "github.com/smartcontractkit/capabilities/chain_capabilities/common/transmission_schedule" "github.com/smartcontractkit/capabilities/chain_capabilities/stellar/actions" @@ -29,6 +30,7 @@ import ( stellarcapserver "github.com/smartcontractkit/chainlink-common/pkg/capabilities/v2/chain-capabilities/stellar/server" "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/loop" + "github.com/smartcontractkit/chainlink-common/pkg/resourcemanager" "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink-common/pkg/settings/limits" "github.com/smartcontractkit/chainlink-common/pkg/types" @@ -53,6 +55,12 @@ type capabilityGRPCService struct { capability lggr logger.Logger limitsFactory limits.Factory + + // meteringCfg comes from the LOOP environment ([Metering] on the host). + // gasMeter emits cre:workflow:gas usage records for write reports when + // MeterRecordsEnabled; nil otherwise. + meteringCfg resourcemanager.Config + gasMeter *gasmeter.Meter } type capability struct { @@ -77,6 +85,7 @@ func main() { return stellarcapserver.NewClientServer(&capabilityGRPCService{ lggr: s.Logger.Named(CapabilityName), limitsFactory: s.LimitsFactory, + meteringCfg: s.MeteringConfig(), }) }, loop.WithOtelViews(consMetrics.MetricViews())) } @@ -111,6 +120,9 @@ func (c *capabilityGRPCService) Close() error { if c.Stellar != nil { closers = append(closers, c.Stellar) } + if c.gasMeter != nil { + closers = append(closers, c.gasMeter) + } return services.CloseAll(closers...) } @@ -244,8 +256,14 @@ func (c *capabilityGRPCService) Initialise(ctx context.Context, dependencies cor if err != nil { return err } + c.gasMeter = gasmeter.New(c.lggr, c.meteringCfg, c.chainSelector, dependencies.CapabilityDonID) + c.WithGasMeter(c.gasMeter) - for _, service := range []interface{ Start(context.Context) error }{c.requestPoller, c.consensusHandler, c.blocksProvider, c.oracle} { + toStart := []interface{ Start(context.Context) error }{c.requestPoller, c.consensusHandler, c.blocksProvider, c.oracle} + if c.gasMeter != nil { + toStart = append(toStart, c.gasMeter) + } + for _, service := range toStart { if err = service.Start(ctx); err != nil { return err } From 152585fbc39b8047e2705d47fe577ce0ac40ba31 Mon Sep 17 00:00:00 2001 From: ilija42 <57732589+ilija42@users.noreply.github.com> Date: Thu, 22 Oct 2026 16:31:43 +0200 Subject: [PATCH 3/3] stellar: drop the gas meter wiring from this PR Reverts 014c0d7. The cre:workflow:gas record needs [Metering] on the chain-capabilities DON configs first and will go in its own PR. This PR stays limited to billing the transaction named in the reply. --- chain_capabilities/stellar/actions/actions.go | 8 ----- .../stellar/actions/write_report.go | 23 +++++------- .../stellar/actions/write_report_test.go | 36 ------------------- chain_capabilities/stellar/go.mod | 6 ++-- chain_capabilities/stellar/go.sum | 12 +++---- chain_capabilities/stellar/main.go | 20 +---------- 6 files changed, 19 insertions(+), 86 deletions(-) diff --git a/chain_capabilities/stellar/actions/actions.go b/chain_capabilities/stellar/actions/actions.go index 11c441105..f0b3c9e6c 100644 --- a/chain_capabilities/stellar/actions/actions.go +++ b/chain_capabilities/stellar/actions/actions.go @@ -24,7 +24,6 @@ import ( "github.com/stellar/go-stellar-sdk/xdr" capcommon "github.com/smartcontractkit/capabilities/chain_capabilities/common" - "github.com/smartcontractkit/capabilities/chain_capabilities/common/gasmeter" commonmon "github.com/smartcontractkit/capabilities/chain_capabilities/common/monitoring" ts "github.com/smartcontractkit/capabilities/chain_capabilities/common/transmission_schedule" "github.com/smartcontractkit/capabilities/libs/chainconsensus" @@ -46,7 +45,6 @@ type Stellar struct { reportSizeLimit limits.BoundLimiter[commoncfg.Size] maxResourceFeeLimit limits.BoundLimiter[uint64] transmissionScheduler ts.TransmissionScheduler - gasMeter *gasmeter.Meter } func NewStellar( @@ -85,12 +83,6 @@ func NewStellar( return st, st.initLimiters(limitsFactory) } -// WithGasMeter enables cre:workflow:gas usage MeterRecords for write reports. -// A nil meter disables emission. -func (s *Stellar) WithGasMeter(m *gasmeter.Meter) { - s.gasMeter = m -} - func (s *Stellar) initLimiters(limitsFactory limits.Factory) (err error) { s.reportSizeLimit, err = limits.MakeUpperBoundLimiter(limitsFactory, cresettings.Default.PerWorkflow.ChainWrite.ReportSizeLimit) if err != nil { diff --git a/chain_capabilities/stellar/actions/write_report.go b/chain_capabilities/stellar/actions/write_report.go index 24e137061..3509a8506 100644 --- a/chain_capabilities/stellar/actions/write_report.go +++ b/chain_capabilities/stellar/actions/write_report.go @@ -22,7 +22,6 @@ import ( stellartypes "github.com/smartcontractkit/chainlink-common/pkg/types/chains/stellar" capcommon "github.com/smartcontractkit/capabilities/chain_capabilities/common" - "github.com/smartcontractkit/capabilities/chain_capabilities/common/gasmeter" ts "github.com/smartcontractkit/capabilities/chain_capabilities/common/transmission_schedule" "github.com/smartcontractkit/capabilities/chain_capabilities/stellar/metering" @@ -44,7 +43,6 @@ type writeReport struct { transmissionScheduler ts.TransmissionScheduler messageBuilder *monitoring.MessageBuilder beholderProcessor beholder.ProtoProcessor - gasMeter *gasmeter.Meter } func (s *Stellar) WriteReport( @@ -98,7 +96,6 @@ func (s *Stellar) executeWriteReport( transmissionScheduler: s.transmissionScheduler, messageBuilder: s.messageBuilder, beholderProcessor: s.beholderProcessor, - gasMeter: s.gasMeter, } return wr.execute(ctx, request, metadata, telemetryContext) } @@ -153,7 +150,7 @@ func (wr *writeReport) execute( if err != nil { return nil, capabilities.ResponseMetadata{}, err } - return reply, wr.meteringFromReply(ctx, metadata, reply), nil + return reply, wr.meteringFromReply(reply), nil case TransmissionStateInvalidReceiver: txHash, hashErr := txHashRetriever.GetFailedTransmissionHash(ctx) if hashErr != nil { @@ -168,7 +165,7 @@ func (wr *writeReport) execute( if err != nil { return nil, capabilities.ResponseMetadata{}, revertReplyBuildError(info, transmissionID, err) } - return reply, wr.meteringFromReply(ctx, metadata, reply), nil + return reply, wr.meteringFromReply(reply), nil case TransmissionStateFailed: txHash, hashErr := txHashRetriever.GetFailedTransmissionHash(ctx) if hashErr != nil { @@ -183,7 +180,7 @@ func (wr *writeReport) execute( if err != nil { return nil, capabilities.ResponseMetadata{}, revertReplyBuildError(info, transmissionID, err) } - return reply, wr.meteringFromReply(ctx, metadata, reply), nil + return reply, wr.meteringFromReply(reply), nil case TransmissionStateNotAttempted: case TransmissionStateUnknown: // Unknown state must not authorize spend. @@ -257,7 +254,7 @@ func (wr *writeReport) execute( if err != nil { return nil, capabilities.ResponseMetadata{}, err } - return reply, wr.meteringFromReply(ctx, metadata, reply), nil + return reply, wr.meteringFromReply(reply), nil } txHash, err := txHashRetriever.GetSuccessfulTransmissionHash(ctx) @@ -273,7 +270,7 @@ func (wr *writeReport) execute( if err != nil { return nil, capabilities.ResponseMetadata{}, err } - return reply, wr.meteringFromReply(ctx, metadata, reply), nil + return reply, wr.meteringFromReply(reply), nil case TransmissionStateFailed, TransmissionStateInvalidReceiver: if submitResp.TxStatus == stellartypes.TxSuccess && submitResp.TxHash != "" { wr.lggr.Errorw("Made a new transmission attempt - transmission failed", "txHash", submitResp.TxHash, "transmissionState", postInfo.State) @@ -281,7 +278,7 @@ func (wr *writeReport) execute( if err != nil { return nil, capabilities.ResponseMetadata{}, revertReplyBuildError(postInfo, transmissionID, err) } - return reply, wr.meteringFromReply(ctx, metadata, reply), nil + return reply, wr.meteringFromReply(reply), nil } txHash, err := txHashRetriever.GetFailedTransmissionHash(ctx) @@ -303,7 +300,7 @@ func (wr *writeReport) execute( if err != nil { return nil, capabilities.ResponseMetadata{}, revertReplyBuildError(postInfo, transmissionID, err) } - return reply, wr.meteringFromReply(ctx, metadata, reply), nil + return reply, wr.meteringFromReply(reply), nil default: wr.lggr.Errorw("Invalid transmission state after submit", "state", postInfo.State, "localTxStatus", submitResp.TxStatus) wr.emitInvalidTransmissionState(ctx, request, telemetryContext, postInfo, transmissionID, "WriteReport invalid transmission state after submit", invalidTransmissionStateError(postInfo.State).Error()) @@ -438,14 +435,12 @@ func (wr *writeReport) pollTransmissionInfo( // meteringFromReply returns billing metadata carrying the on-chain fee of the transaction the // reply names, so every node bills the same canonical transaction whether it submitted it, -// returned early for it, or sent a duplicate. It also emits the cre:workflow:gas usage record -// for that transaction. An absent fee yields empty metadata and no record. -func (wr *writeReport) meteringFromReply(ctx context.Context, metadata capabilities.RequestMetadata, reply *stellarcap.WriteReportReply) capabilities.ResponseMetadata { +// returned early for it, or sent a duplicate. An absent fee yields empty metadata. +func (wr *writeReport) meteringFromReply(reply *stellarcap.WriteReportReply) capabilities.ResponseMetadata { if reply.TransactionFee == nil { wr.lggr.Warnw("Transaction fee unavailable in reply; skipping metering", "txHash", reply.GetTxHash()) return capabilities.ResponseMetadata{} } - wr.gasMeter.Emit(ctx, metadata, reply.GetTxHash(), new(big.Int).SetUint64(*reply.TransactionFee)) return metering.GetResponseMetadataWriteReport(*reply.TransactionFee, wr.chainSelector) } diff --git a/chain_capabilities/stellar/actions/write_report_test.go b/chain_capabilities/stellar/actions/write_report_test.go index 618be4a9a..aa638ad88 100644 --- a/chain_capabilities/stellar/actions/write_report_test.go +++ b/chain_capabilities/stellar/actions/write_report_test.go @@ -19,13 +19,11 @@ import ( p2ptypes "github.com/smartcontractkit/libocr/ragep2p/types" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - "go.uber.org/zap/zapcore" "github.com/smartcontractkit/chainlink-common/pkg/capabilities" ocrtypes "github.com/smartcontractkit/chainlink-common/pkg/capabilities/consensus/ocr3/types" stellarcap "github.com/smartcontractkit/chainlink-common/pkg/capabilities/v2/chain-capabilities/stellar" "github.com/smartcontractkit/chainlink-common/pkg/logger" - "github.com/smartcontractkit/chainlink-common/pkg/resourcemanager" "github.com/smartcontractkit/chainlink-common/pkg/settings/limits" "github.com/smartcontractkit/chainlink-common/pkg/types" stellartypes "github.com/smartcontractkit/chainlink-common/pkg/types/chains/stellar" @@ -33,7 +31,6 @@ import ( workflowpb "github.com/smartcontractkit/chainlink-protos/cre/go/sdk" capcommon "github.com/smartcontractkit/capabilities/chain_capabilities/common" - "github.com/smartcontractkit/capabilities/chain_capabilities/common/gasmeter" commontest "github.com/smartcontractkit/capabilities/chain_capabilities/common/test" ts "github.com/smartcontractkit/capabilities/chain_capabilities/common/transmission_schedule" @@ -1935,36 +1932,3 @@ func TestCheckEstimatedSpendLimit(t *testing.T) { require.Contains(t, err.Error(), "invalid spend limit") }) } - -type discardEmitter struct{} - -func (discardEmitter) Emit(context.Context, []byte, ...any) error { return nil } - -func TestMeteringFromReply_EmitsGasUsageRecord(t *testing.T) { - t.Parallel() - lggr, obs := logger.TestObserved(t, zapcore.InfoLevel) - meter := gasmeter.New(lggr, resourcemanager.Config{ - MeterRecordsEnabled: true, - Emitter: discardEmitter{}, - DeploymentIdentity: resourcemanager.DeploymentIdentity{Product: "cre", Tenant: "t", Environment: "test", Zone: "z", NodeID: "node-1"}, - }, testWRChainSelector, 7) - require.NotNil(t, meter) - wr := &writeReport{lggr: logger.Sugared(lggr), chainSelector: testWRChainSelector, gasMeter: meter} - metadata := capabilities.RequestMetadata{WorkflowID: "wf-1", WorkflowExecutionID: "exec-1", OrgID: "org-1"} - - meta := wr.meteringFromReply(t.Context(), metadata, &stellarcap.WriteReportReply{TxHash: new(testTxHash), TransactionFee: new(testFee)}) - - validateWRMetering(t, meta, testWRChainSelector, testFee) - logs := obs.FilterMessage("Emitted capability usage meter record").All() - require.Len(t, logs, 1) - fields := logs[0].ContextMap() - require.Equal(t, "exec-1", fields["executionID"]) - require.Equal(t, testTxHash, fields["txHash"]) - require.Equal(t, fmt.Sprintf("cre:workflow:gas:%d", testWRChainSelector), fields["resourceType"]) - require.Equal(t, strconv.FormatUint(testFee, 10), fields["value"]) - - // Without a meter the metering metadata is unchanged and nothing is emitted. - meta = (&writeReport{lggr: logger.Sugared(lggr), chainSelector: testWRChainSelector}).meteringFromReply(t.Context(), metadata, &stellarcap.WriteReportReply{TxHash: new(testTxHash), TransactionFee: new(testFee)}) - validateWRMetering(t, meta, testWRChainSelector, testFee) - require.Len(t, obs.FilterMessage("Emitted capability usage meter record").All(), 1) -} diff --git a/chain_capabilities/stellar/go.mod b/chain_capabilities/stellar/go.mod index bdef2ad95..d28f55425 100644 --- a/chain_capabilities/stellar/go.mod +++ b/chain_capabilities/stellar/go.mod @@ -3,10 +3,10 @@ module github.com/smartcontractkit/capabilities/chain_capabilities/stellar go 1.27.3 require ( - github.com/smartcontractkit/capabilities/chain_capabilities/common v0.0.0-20261009182120-372f846f528c + github.com/smartcontractkit/capabilities/chain_capabilities/common v0.0.0-20260922114021-5cee95428866 github.com/smartcontractkit/capabilities/libs v0.0.0-20261001142216-b1dea8dbb961 github.com/smartcontractkit/chain-selectors v1.0.104 - github.com/smartcontractkit/chainlink-common v0.11.2-0.20261006175240-29594528f464 + github.com/smartcontractkit/chainlink-common v0.11.2-0.20260925190414-e1b87ad2d7b1 github.com/smartcontractkit/chainlink-framework/multinode v0.0.0-20260521164805-26d78d5e1243 github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260821155228-fa1d775e2138 github.com/smartcontractkit/libocr v0.0.0-20260810200708-618b5bf7f342 @@ -80,7 +80,7 @@ require ( github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 // indirect github.com/scylladb/go-reflectx v1.0.1 // indirect github.com/shopspring/decimal v1.4.0 // indirect - github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.20261005132539-b7d63d5fd549 // indirect + github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.20260915184316-2730f1867c92 // indirect github.com/smartcontractkit/chainlink-framework/metrics v0.0.0-20260401162955-be2bc6b5264b // indirect github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20251002192024-d2ad9222409b // indirect github.com/smartcontractkit/chainlink-protos/metering/go v0.0.0-20260710151514-27b5a126dabe // indirect diff --git a/chain_capabilities/stellar/go.sum b/chain_capabilities/stellar/go.sum index 25dba54f6..27fe98531 100644 --- a/chain_capabilities/stellar/go.sum +++ b/chain_capabilities/stellar/go.sum @@ -206,16 +206,16 @@ github.com/scylladb/go-reflectx v1.0.1 h1:b917wZM7189pZdlND9PbIJ6NQxfDPfBvUaQ7cj github.com/scylladb/go-reflectx v1.0.1/go.mod h1:rWnOfDIRWBGN0miMLIcoPt/Dhi2doCMZqwMCJ3KupFc= github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= -github.com/smartcontractkit/capabilities/chain_capabilities/common v0.0.0-20261009182120-372f846f528c h1:R8cKOczBWJ31Spy7ii/+Im269YeWbk6oJ33vDd6cJ3Q= -github.com/smartcontractkit/capabilities/chain_capabilities/common v0.0.0-20261009182120-372f846f528c/go.mod h1:ubztvmJnKJilCw4CzrlHSkKvPYXNeUArML/xY1VzsBg= +github.com/smartcontractkit/capabilities/chain_capabilities/common v0.0.0-20260922114021-5cee95428866 h1:nJ3V1j6QSFJ3zfwTO8Ty5TVNEb7g4HUkr9VXXV7hLSg= +github.com/smartcontractkit/capabilities/chain_capabilities/common v0.0.0-20260922114021-5cee95428866/go.mod h1:URIXl+ew48QapONMSLC1faCcGRnm5CRL3jj1PtpJt5E= github.com/smartcontractkit/capabilities/libs v0.0.0-20261001142216-b1dea8dbb961 h1:2+0wOWEiYcEm0XlS1HwgH09y/3qZ0zjokTdbExY2VQQ= github.com/smartcontractkit/capabilities/libs v0.0.0-20261001142216-b1dea8dbb961/go.mod h1:h4kazMLsDM6CsKl2OnT9AtCHhhRsFptA/OTM7Hkj2GE= github.com/smartcontractkit/chain-selectors v1.0.104 h1:/n9pPGM5W/+r1eHoWZv4VwX9LNS1af4+ICyhM8zKRNM= github.com/smartcontractkit/chain-selectors v1.0.104/go.mod h1:qy7whtgG5g+7z0jt0nRyii9bLND9m15NZTzuQPkMZ5w= -github.com/smartcontractkit/chainlink-common v0.11.2-0.20261006175240-29594528f464 h1:DVXwyIGekGqLaFAcSWa3K3S209gAY0WDFwliYdLp1dk= -github.com/smartcontractkit/chainlink-common v0.11.2-0.20261006175240-29594528f464/go.mod h1:37Mu0pbAnGmHPgOZ8bQvgP4hzXw/Wu7smKX4cN46UOs= -github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.20261005132539-b7d63d5fd549 h1:J4vYRJxkBO8Yf//17wScKjYWmS45VZaFWx0zt+4ETEU= -github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.20261005132539-b7d63d5fd549/go.mod h1:Scyk7O80fde5rJF3Ty3fEE3xiS6j84eWbu8/2gl3pIY= +github.com/smartcontractkit/chainlink-common v0.11.2-0.20260925190414-e1b87ad2d7b1 h1:1rGIv4vUt+Et7k5waRbkODOQsMuEakQ8TA9PiMBaPss= +github.com/smartcontractkit/chainlink-common v0.11.2-0.20260925190414-e1b87ad2d7b1/go.mod h1:UkZJ6uUU9Hlonfb+k62Pg4yRuvOsoShoOd4tPEB/qpE= +github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.20260915184316-2730f1867c92 h1:Z1OIGukDhJD+Gknr5G07Vtml24E7BwKaIQoWzV/SdqA= +github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.20260915184316-2730f1867c92/go.mod h1:Scyk7O80fde5rJF3Ty3fEE3xiS6j84eWbu8/2gl3pIY= github.com/smartcontractkit/chainlink-framework/metrics v0.0.0-20260401162955-be2bc6b5264b h1:L1So1EDBDRET3j/TdV1Gjv3qWARoa/NPRaU7k4r30yA= github.com/smartcontractkit/chainlink-framework/metrics v0.0.0-20260401162955-be2bc6b5264b/go.mod h1:HG/aei0MgBOpsyRLexdKGtOUO8yjSJO3iUu0Uu8KBm4= github.com/smartcontractkit/chainlink-framework/multinode v0.0.0-20260521164805-26d78d5e1243 h1:71PGTkjdFZ0JrloEC2Fs8eHl1b1gmUuH+bq7q23usKk= diff --git a/chain_capabilities/stellar/main.go b/chain_capabilities/stellar/main.go index 825a0264d..84e95299c 100644 --- a/chain_capabilities/stellar/main.go +++ b/chain_capabilities/stellar/main.go @@ -18,7 +18,6 @@ import ( "github.com/smartcontractkit/capabilities/libs/loopserver" capcommon "github.com/smartcontractkit/capabilities/chain_capabilities/common" - "github.com/smartcontractkit/capabilities/chain_capabilities/common/gasmeter" ts "github.com/smartcontractkit/capabilities/chain_capabilities/common/transmission_schedule" "github.com/smartcontractkit/capabilities/chain_capabilities/stellar/actions" @@ -30,7 +29,6 @@ import ( stellarcapserver "github.com/smartcontractkit/chainlink-common/pkg/capabilities/v2/chain-capabilities/stellar/server" "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/loop" - "github.com/smartcontractkit/chainlink-common/pkg/resourcemanager" "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink-common/pkg/settings/limits" "github.com/smartcontractkit/chainlink-common/pkg/types" @@ -55,12 +53,6 @@ type capabilityGRPCService struct { capability lggr logger.Logger limitsFactory limits.Factory - - // meteringCfg comes from the LOOP environment ([Metering] on the host). - // gasMeter emits cre:workflow:gas usage records for write reports when - // MeterRecordsEnabled; nil otherwise. - meteringCfg resourcemanager.Config - gasMeter *gasmeter.Meter } type capability struct { @@ -85,7 +77,6 @@ func main() { return stellarcapserver.NewClientServer(&capabilityGRPCService{ lggr: s.Logger.Named(CapabilityName), limitsFactory: s.LimitsFactory, - meteringCfg: s.MeteringConfig(), }) }, loop.WithOtelViews(consMetrics.MetricViews())) } @@ -120,9 +111,6 @@ func (c *capabilityGRPCService) Close() error { if c.Stellar != nil { closers = append(closers, c.Stellar) } - if c.gasMeter != nil { - closers = append(closers, c.gasMeter) - } return services.CloseAll(closers...) } @@ -256,14 +244,8 @@ func (c *capabilityGRPCService) Initialise(ctx context.Context, dependencies cor if err != nil { return err } - c.gasMeter = gasmeter.New(c.lggr, c.meteringCfg, c.chainSelector, dependencies.CapabilityDonID) - c.WithGasMeter(c.gasMeter) - toStart := []interface{ Start(context.Context) error }{c.requestPoller, c.consensusHandler, c.blocksProvider, c.oracle} - if c.gasMeter != nil { - toStart = append(toStart, c.gasMeter) - } - for _, service := range toStart { + for _, service := range []interface{ Start(context.Context) error }{c.requestPoller, c.consensusHandler, c.blocksProvider, c.oracle} { if err = service.Start(ctx); err != nil { return err }