From 4da7faad0635ffc6ed1b544c8945f3c786529262 Mon Sep 17 00:00:00 2001 From: Vladimir Shchukin Date: Thu, 4 Jun 2026 12:00:56 -0400 Subject: [PATCH 01/16] Solana actions initial implementation --- chain_capabilities/solana/actions/actions.go | 285 ++++++++++++++++--- chain_capabilities/solana/go.mod | 8 +- chain_capabilities/solana/go.sum | 16 +- 3 files changed, 260 insertions(+), 49 deletions(-) diff --git a/chain_capabilities/solana/actions/actions.go b/chain_capabilities/solana/actions/actions.go index d5b9a0036..777fc65b7 100644 --- a/chain_capabilities/solana/actions/actions.go +++ b/chain_capabilities/solana/actions/actions.go @@ -4,11 +4,13 @@ import ( "context" "errors" "fmt" + "math/big" "strings" "time" "github.com/smartcontractkit/capabilities/chain_capabilities/solana/metering" "github.com/smartcontractkit/capabilities/libs/chainconsensus" + commonMon "github.com/smartcontractkit/capabilities/libs/monitoring" ctypes "github.com/smartcontractkit/capabilities/libs/chainconsensus/types" "github.com/smartcontractkit/chainlink-common/pkg/beholder" @@ -21,6 +23,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/settings/limits" "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink-framework/multinode" + valuespb "github.com/smartcontractkit/chainlink-protos/cre/go/values/pb" capcommon "github.com/smartcontractkit/capabilities/chain_capabilities/common" ts "github.com/smartcontractkit/capabilities/chain_capabilities/common/transmission_schedule" @@ -108,85 +111,264 @@ func (s *Solana) GetAccountInfoWithOpts( return responseAndMetadata, nil } -func getReadError(lggr logger.SugaredLogger, err error) caperrors.Error { - if err == nil { - return nil +func (s *Solana) GetBalance( + ctx context.Context, + metadata capabilities.RequestMetadata, + input *solcap.GetBalanceRequest) (*capabilities.ResponseAndMetadata[*solcap.GetBalanceReply], caperrors.Error) { + if !s.readsEnabled { + return nil, caperrors.NewPublicSystemError(errors.New("reads are not available"), caperrors.Internal) } - - isUserErr := isUserError(err) - capErr := GetError(err, isUserErr) - - // TODO: logging of init, success and error should be move to a higher level - lggr = lggr.With("error", err) - const msg = "Read operation failed" - if isUserErr { - lggr.Debug(msg) - } else { - lggr.Error(msg) + request, err := solcap.ConvertGetBalanceRequestFromProto(input) + if err != nil { + return nil, NewUserError(fmt.Errorf("invalid request: %w", err)) } - return capErr -} + lggr := s.messageBuilder.RequestLggr(s.lggr, monitoring.TelemetryContext{TsStart: time.Now().UnixMilli(), RequestMetadata: metadata}).With("request", request) + lggr.Debugw("Received GetBalance request") + cReq := ctypes.NewVolatileRequest(metadata.WorkflowExecutionID, metadata.ReferenceID, metering.GetResponseMetadata(metering.GetAccountInfo), func(ctx context.Context) (*solcap.GetBalanceReply, uint64, error) { + rawResponse, err := s.SolanaService.GetBalance(ctx, request) + if err != nil { + return nil, 0, err + } -func isUserError(err error) bool { - return !errors.Is(err, context.DeadlineExceeded) && !isNodeInfraError(err) -} + response, err := solcap.ConvertGetBalanceReplyToProto(rawResponse) + if err != nil { + return nil, 0, caperrors.NewPublicSystemError(fmt.Errorf("failed to convert response to proto: %w", err), caperrors.Internal) + } -func isNodeInfraError(err error) bool { - return errors.Is(err, multinode.ErrNodeError) || - strings.Contains(err.Error(), multinode.ErrNodeError.Error()) -} + return response, 0, nil + }, lggr) + responseAndMetadata, err := chainconsensus.ReadHashableRequestReport[*solcap.GetBalanceReply](ctx, s.handler, cReq) + if err != nil { + return nil, getReadError(lggr, fmt.Errorf("failed to GetBalance: %w", err)) + } -func (s *Solana) GetBalance( - ctx context.Context, - metadata capabilities.RequestMetadata, - input *solcap.GetBalanceRequest) (*capabilities.ResponseAndMetadata[*solcap.GetBalanceReply], caperrors.Error) { - // TODO - return nil, GetError(errors.New("unimplemented"), false) + lggr.Debugw("Successfully handled GetBalance") + return responseAndMetadata, nil } func (s *Solana) GetBlock( ctx context.Context, metadata capabilities.RequestMetadata, input *solcap.GetBlockRequest) (*capabilities.ResponseAndMetadata[*solcap.GetBlockReply], caperrors.Error) { - // TODO - return nil, GetError(errors.New("unimplemented"), false) + if !s.readsEnabled { + return nil, caperrors.NewPublicSystemError(errors.New("reads are not available"), caperrors.Internal) + } + request, err := solcap.ConvertGetBlockRequestFromProto(input) + if err != nil { + return nil, NewUserError(fmt.Errorf("invalid request: %w", err)) + } + + lggr := s.messageBuilder.RequestLggr(s.lggr, monitoring.TelemetryContext{TsStart: time.Now().UnixMilli(), RequestMetadata: metadata}).With("request", request) + lggr.Debugw("Received GetBlock request") + cReq := ctypes.NewECHashableRequest(metadata.WorkflowExecutionID, metadata.ReferenceID, metering.GetResponseMetadata(metering.GetAccountInfo), func(ctx context.Context) (*solcap.GetBlockReply, error) { + rawResponse, err := s.SolanaService.GetBlock(ctx, *request) + if err != nil { + return nil, err + } + + response, err := solcap.ConvertGetBlockReplyToProto(rawResponse) + if err != nil { + return nil, caperrors.NewPublicSystemError(fmt.Errorf("failed to convert response to proto: %w", err), caperrors.Internal) + } + + return response, nil + }) + responseAndMetadata, err := chainconsensus.ReadHashableRequestReport[*solcap.GetBlockReply](ctx, s.handler, cReq) + if err != nil { + return nil, getReadError(lggr, fmt.Errorf("failed to GetBlock: %w", err)) + } + + lggr.Debugw("Successfully handled GetBlock") + return responseAndMetadata, nil } func (s *Solana) GetFeeForMessage( ctx context.Context, metadata capabilities.RequestMetadata, input *solcap.GetFeeForMessageRequest) (*capabilities.ResponseAndMetadata[*solcap.GetFeeForMessageReply], caperrors.Error) { - // TODO - return nil, GetError(errors.New("unimplemented"), false) + if !s.readsEnabled { + return nil, caperrors.NewPublicSystemError(errors.New("reads are not available"), caperrors.Internal) + } + request, err := solcap.ConvertGetFeeForMessageRequestFromProto(input) + if err != nil { + return nil, NewUserError(fmt.Errorf("invalid request: %w", err)) + } + + lggr := s.messageBuilder.RequestLggr(s.lggr, monitoring.TelemetryContext{TsStart: time.Now().UnixMilli(), RequestMetadata: metadata}).With("request", request) + lggr.Debugw("Received GetFeeForMessage request") + cReq := ctypes.NewAggregatableRequest(commonMon.RequestID(metadata.WorkflowExecutionID, metadata.ReferenceID), func(ctx context.Context) (*ctypes.AggregatableObservation, error) { + rawResponse, err := s.SolanaService.GetFeeForMessage(ctx, *request) + if err != nil { + return nil, err + } + + return &ctypes.AggregatableObservation{ + Method: ctypes.AggregationMethodFPlusOneHighest, + Value: &valuespb.Decimal{ + Coefficient: valuespb.NewBigIntFromInt(new(big.Int).SetUint64(rawResponse.Fee)), + Exponent: 0, + }, + }, nil + }) + aggregatedFee, err := chainconsensus.ReadDecimal(ctx, s.handler, cReq) + if err != nil { + return nil, getReadError(lggr, fmt.Errorf("failed to GetFeeForMessage: %w", err)) + } + + lggr.Debugw("Successfully handled GetFeeForMessage") + return &capabilities.ResponseAndMetadata[*solcap.GetFeeForMessageReply]{ + Response: &solcap.GetFeeForMessageReply{Fee: aggregatedFee.BigInt().Uint64()}, + ResponseMetadata: metering.GetResponseMetadata(metering.GetAccountInfo), + }, nil } func (s *Solana) GetMultipleAccountsWithOpts( ctx context.Context, metadata capabilities.RequestMetadata, input *solcap.GetMultipleAccountsWithOptsRequest) (*capabilities.ResponseAndMetadata[*solcap.GetMultipleAccountsWithOptsReply], caperrors.Error) { - return nil, GetError(errors.New("unimplemented"), false) + if !s.readsEnabled { + return nil, caperrors.NewPublicSystemError(errors.New("reads are not available"), caperrors.Internal) + } + request, err := solcap.ConvertGetMultipleAccountsRequestFromProto(input) + if err != nil { + return nil, NewUserError(fmt.Errorf("invalid request: %w", err)) + } + + lggr := s.messageBuilder.RequestLggr(s.lggr, monitoring.TelemetryContext{TsStart: time.Now().UnixMilli(), RequestMetadata: metadata}).With("request", request) + lggr.Debugw("Received GetMultipleAccountsWithOpts request") + cReq := ctypes.NewVolatileRequest(metadata.WorkflowExecutionID, metadata.ReferenceID, metering.GetResponseMetadata(metering.GetAccountInfo), func(ctx context.Context) (*solcap.GetMultipleAccountsWithOptsReply, uint64, error) { + rawResponse, err := s.SolanaService.GetMultipleAccountsWithOpts(ctx, *request) + if err != nil { + return nil, 0, err + } + + response, err := solcap.ConvertGetMultipleAccountsReplyToProto(rawResponse) + if err != nil { + return nil, 0, caperrors.NewPublicSystemError(fmt.Errorf("failed to convert response to proto: %w", err), caperrors.Internal) + } + + return response, rawResponse.Slot, nil + }, lggr) + responseAndMetadata, err := chainconsensus.ReadHashableRequestReport[*solcap.GetMultipleAccountsWithOptsReply](ctx, s.handler, cReq) + if err != nil { + return nil, getReadError(lggr, fmt.Errorf("failed to GetMultipleAccountsWithOpts: %w", err)) + } + + lggr.Debugw("Successfully handled GetMultipleAccountsWithOpts") + return responseAndMetadata, nil } func (s *Solana) GetSignatureStatuses( ctx context.Context, metadata capabilities.RequestMetadata, input *solcap.GetSignatureStatusesRequest) (*capabilities.ResponseAndMetadata[*solcap.GetSignatureStatusesReply], caperrors.Error) { - return nil, GetError(errors.New("unimplemented"), false) + if !s.readsEnabled { + return nil, caperrors.NewPublicSystemError(errors.New("reads are not available"), caperrors.Internal) + } + request, err := solcap.ConvertGetSignatureStatusesRequestFromProto(input) + if err != nil { + return nil, NewUserError(fmt.Errorf("invalid request: %w", err)) + } + + lggr := s.messageBuilder.RequestLggr(s.lggr, monitoring.TelemetryContext{TsStart: time.Now().UnixMilli(), RequestMetadata: metadata}).With("request", request) + lggr.Debugw("Received GetSignatureStatuses request") + cReq := ctypes.NewECHashableRequest(metadata.WorkflowExecutionID, metadata.ReferenceID, metering.GetResponseMetadata(metering.GetAccountInfo), func(ctx context.Context) (*solcap.GetSignatureStatusesReply, error) { + rawResponse, err := s.SolanaService.GetSignatureStatuses(ctx, *request) + if err != nil { + return nil, err + } + + response, err := solcap.ConvertGetSignatureStatusesReplyToProto(rawResponse) + if err != nil { + return nil, caperrors.NewPublicSystemError(fmt.Errorf("failed to convert response to proto: %w", err), caperrors.Internal) + } + + return response, nil + }) + responseAndMetadata, err := chainconsensus.ReadHashableRequestReport[*solcap.GetSignatureStatusesReply](ctx, s.handler, cReq) + if err != nil { + return nil, getReadError(lggr, fmt.Errorf("failed to GetSignatureStatuses: %w", err)) + } + + lggr.Debugw("Successfully handled GetSignatureStatuses") + return responseAndMetadata, nil } func (s *Solana) GetSlotHeight( ctx context.Context, metadata capabilities.RequestMetadata, input *solcap.GetSlotHeightRequest) (*capabilities.ResponseAndMetadata[*solcap.GetSlotHeightReply], caperrors.Error) { - return nil, GetError(errors.New("unimplemented"), false) + if !s.readsEnabled { + return nil, caperrors.NewPublicSystemError(errors.New("reads are not available"), caperrors.Internal) + } + request, err := solcap.ConvertGetSlotHeightRequestFromProto(input) + if err != nil { + return nil, NewUserError(fmt.Errorf("invalid request: %w", err)) + } + + lggr := s.messageBuilder.RequestLggr(s.lggr, monitoring.TelemetryContext{TsStart: time.Now().UnixMilli(), RequestMetadata: metadata}).With("request", request) + lggr.Debugw("Received GetSlotHeight request") + cReq := ctypes.NewAggregatableRequest(commonMon.RequestID(metadata.WorkflowExecutionID, metadata.ReferenceID), func(ctx context.Context) (*ctypes.AggregatableObservation, error) { + rawResponse, err := s.SolanaService.GetSlotHeight(ctx, request) + if err != nil { + return nil, err + } + + return &ctypes.AggregatableObservation{ + Method: ctypes.AggregationMethodFPlusOneHighest, + Value: &valuespb.Decimal{ + Coefficient: valuespb.NewBigIntFromInt(new(big.Int).SetUint64(rawResponse.Height)), + Exponent: 0, + }, + }, nil + }) + aggregatedHeight, err := chainconsensus.ReadDecimal(ctx, s.handler, cReq) + if err != nil { + return nil, getReadError(lggr, fmt.Errorf("failed to GetSlotHeight: %w", err)) + } + + lggr.Debugw("Successfully handled GetSlotHeight") + return &capabilities.ResponseAndMetadata[*solcap.GetSlotHeightReply]{ + Response: &solcap.GetSlotHeightReply{Height: aggregatedHeight.BigInt().Uint64()}, + ResponseMetadata: metering.GetResponseMetadata(metering.GetAccountInfo), + }, nil } func (s *Solana) GetTransaction( ctx context.Context, metadata capabilities.RequestMetadata, input *solcap.GetTransactionRequest) (*capabilities.ResponseAndMetadata[*solcap.GetTransactionReply], caperrors.Error) { - return nil, GetError(errors.New("unimplemented"), false) + if !s.readsEnabled { + return nil, caperrors.NewPublicSystemError(errors.New("reads are not available"), caperrors.Internal) + } + request, err := solcap.ConvertGetTransactionRequestFromProto(input) + if err != nil { + return nil, NewUserError(fmt.Errorf("invalid request: %w", err)) + } + + lggr := s.messageBuilder.RequestLggr(s.lggr, monitoring.TelemetryContext{TsStart: time.Now().UnixMilli(), RequestMetadata: metadata}).With("request", request) + lggr.Debugw("Received GetTransaction request") + cReq := ctypes.NewECHashableRequest(metadata.WorkflowExecutionID, metadata.ReferenceID, metering.GetResponseMetadata(metering.GetAccountInfo), func(ctx context.Context) (*solcap.GetTransactionReply, error) { + rawResponse, err := s.SolanaService.GetTransaction(ctx, request) + if err != nil { + return nil, err + } + + response, err := solcap.ConvertGetTransactionReplyToProto(rawResponse) + if err != nil { + return nil, caperrors.NewPublicSystemError(fmt.Errorf("failed to convert response to proto: %w", err), caperrors.Internal) + } + + return response, nil + }) + responseAndMetadata, err := chainconsensus.ReadHashableRequestReport[*solcap.GetTransactionReply](ctx, s.handler, cReq) + if err != nil { + return nil, getReadError(lggr, fmt.Errorf("failed to GetTransaction: %w", err)) + } + + lggr.Debugw("Successfully handled GetTransaction") + return responseAndMetadata, nil } func (s *Solana) initLimiters(limitsFactory limits.Factory) (err error) { @@ -203,5 +385,34 @@ func (s *Solana) initLimiters(limitsFactory limits.Factory) (err error) { return } +func getReadError(lggr logger.SugaredLogger, err error) caperrors.Error { + if err == nil { + return nil + } + + isUserErr := isUserError(err) + capErr := GetError(err, isUserErr) + + // TODO: logging of init, success and error should be move to a higher level + lggr = lggr.With("error", err) + const msg = "Read operation failed" + if isUserErr { + lggr.Debug(msg) + } else { + lggr.Error(msg) + } + + return capErr +} + +func isUserError(err error) bool { + return !errors.Is(err, context.DeadlineExceeded) && !isNodeInfraError(err) +} + +func isNodeInfraError(err error) bool { + return errors.Is(err, multinode.ErrNodeError) || + strings.Contains(err.Error(), multinode.ErrNodeError.Error()) +} + var GetError = capcommon.GetError var NewUserError = capcommon.NewUserError diff --git a/chain_capabilities/solana/go.mod b/chain_capabilities/solana/go.mod index 5b80fb145..0c5ac3b28 100644 --- a/chain_capabilities/solana/go.mod +++ b/chain_capabilities/solana/go.mod @@ -6,12 +6,12 @@ require ( github.com/gagliardetto/solana-go v1.14.0 github.com/google/go-cmp v0.7.0 github.com/mr-tron/base58 v1.2.0 - github.com/smartcontractkit/capabilities/chain_capabilities/common v0.0.0-20260602135542-db372a4c73d4 - github.com/smartcontractkit/capabilities/libs v0.0.0-20260602135542-db372a4c73d4 + github.com/smartcontractkit/capabilities/chain_capabilities/common v0.0.0-20260601161303-0b4b1ac1c3fb + github.com/smartcontractkit/capabilities/libs v0.0.0-20260604135015-e9711af26f89 github.com/smartcontractkit/chain-selectors v1.0.100 - github.com/smartcontractkit/chainlink-common v0.11.2-0.20260602135221-cc7a5b50532a + github.com/smartcontractkit/chainlink-common v0.11.2-0.20260604133138-930561c898fc github.com/smartcontractkit/chainlink-framework/multinode v0.0.0-20260326180413-c69f27e37a13 - github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260526195338-adcf8013a1b7 + github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260602131523-5168ac1ba014 github.com/smartcontractkit/chainlink-solana v1.1.2-0.20260420191419-ea62f88cbdb4 github.com/smartcontractkit/chainlink-solana/contracts v0.0.0-20260421131224-c46cbfe7bc6c github.com/smartcontractkit/libocr v0.0.0-20260403184524-b6409238958d diff --git a/chain_capabilities/solana/go.sum b/chain_capabilities/solana/go.sum index 544bc7dfd..f02411002 100644 --- a/chain_capabilities/solana/go.sum +++ b/chain_capabilities/solana/go.sum @@ -617,10 +617,10 @@ github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/smartcontractkit/capabilities/chain_capabilities/common v0.0.0-20260602135542-db372a4c73d4 h1:3yqrDt7eG0q09XwEsRBCDzsFZ0c2z5xYUFVfmLBi8MA= -github.com/smartcontractkit/capabilities/chain_capabilities/common v0.0.0-20260602135542-db372a4c73d4/go.mod h1:PvKEGpgXj36MV0zvAiPFbmWuITuiInug+ygVMJ+JsWo= -github.com/smartcontractkit/capabilities/libs v0.0.0-20260602135542-db372a4c73d4 h1:yS513wsePMBCVGlsMoyKb/ilLtOqgq4Md+xDXaKNoRQ= -github.com/smartcontractkit/capabilities/libs v0.0.0-20260602135542-db372a4c73d4/go.mod h1:LS7F8U2YZNc0Vt8f6SVWUUigGLxdxZMpyC7VCcUTagg= +github.com/smartcontractkit/capabilities/chain_capabilities/common v0.0.0-20260601161303-0b4b1ac1c3fb h1:rS8SazTuMNU2dX8D3lPw8dBUIl1nZdcfojIQsBxeC68= +github.com/smartcontractkit/capabilities/chain_capabilities/common v0.0.0-20260601161303-0b4b1ac1c3fb/go.mod h1:bdh6ANWBJiDEC8tp66ZyLxdfTJSdb00Bgu38Fb+nWyE= +github.com/smartcontractkit/capabilities/libs v0.0.0-20260604135015-e9711af26f89 h1:sff0gl1oh8iz+ANmXUpMXyEzF/Q8jYGchsug/FpFG/A= +github.com/smartcontractkit/capabilities/libs v0.0.0-20260604135015-e9711af26f89/go.mod h1:LS7F8U2YZNc0Vt8f6SVWUUigGLxdxZMpyC7VCcUTagg= github.com/smartcontractkit/chain-selectors v1.0.100 h1:wpiSpmI/eFjY+wx/nPr5VuNF4hki0prIBMKEaQWn3g4= github.com/smartcontractkit/chain-selectors v1.0.100/go.mod h1:qy7whtgG5g+7z0jt0nRyii9bLND9m15NZTzuQPkMZ5w= github.com/smartcontractkit/chainlink-ccip v0.1.1-solana.0.20260317185256-d5f7db87ae70 h1:bpzTG/8qwnbnIQPcilnM8lPd/Or4Q22cnakzawds2NQ= @@ -629,8 +629,8 @@ github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20260310183131-8 github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20260310183131-8d0f0e383288/go.mod h1:jPHlo/IN2YAArI001JJixmm6ZHQwgnAVJXY8VBFiFTc= github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20260310183131-8d0f0e383288 h1:eEjTgIQn4RW0ZPRepUDYTdgGwaRCMawMwgXkHItUc9U= github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20260310183131-8d0f0e383288/go.mod h1:67YbnoglYD61Pz/jTVCgav9wFq7S35OU8UyQSvPllRw= -github.com/smartcontractkit/chainlink-common v0.11.2-0.20260602135221-cc7a5b50532a h1:DHdGiDkMe+WS2e+Cy6ooNxGVX4gwiRRcgperFWmR4R4= -github.com/smartcontractkit/chainlink-common v0.11.2-0.20260602135221-cc7a5b50532a/go.mod h1:6jgqiFXFJHqjkvFFmuf8gvoUFa6Ygx/D1tKnIL+CCF8= +github.com/smartcontractkit/chainlink-common v0.11.2-0.20260604133138-930561c898fc h1:bW6w4k8BWz+2KF3vVM45IekL5Ul3yiNEBkeKHDYUOx0= +github.com/smartcontractkit/chainlink-common v0.11.2-0.20260604133138-930561c898fc/go.mod h1:zC5csAXmmn2FZbZ78Rrfc4AvmEKJzKQLMEY/w2SRwDo= github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.20260601211238-9f526774fef0 h1:NExKM/D0HneOq/N5LGTbkV4VOa0UHCvfTNEb4GqYpto= github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.20260601211238-9f526774fef0/go.mod h1:HmUyH2oD9m+GRpKq7q3vuRnm1F2Uczf/Nd1v3ipMSK8= github.com/smartcontractkit/chainlink-common/pkg/monitoring v0.0.0-20251215152504-b1e41f508340 h1:PsjEI+5jZIz9AS4eOsLS5VpSWJINf38clXV3wryPyMk= @@ -641,8 +641,8 @@ github.com/smartcontractkit/chainlink-framework/multinode v0.0.0-20260326180413- github.com/smartcontractkit/chainlink-framework/multinode v0.0.0-20260326180413-c69f27e37a13/go.mod h1:Y7h84PqCe/Vimf2h1Nc6tMiOJStDbtM33fEUeaaF5xk= github.com/smartcontractkit/chainlink-protos/billing/go v0.0.0-20251024234028-0988426d98f4 h1:GCzrxDWn3b7jFfEA+WiYRi8CKoegsayiDoJBCjYkneE= github.com/smartcontractkit/chainlink-protos/billing/go v0.0.0-20251024234028-0988426d98f4/go.mod h1:HHGeDUpAsPa0pmOx7wrByCitjQ0mbUxf0R9v+g67uCA= -github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260526195338-adcf8013a1b7 h1:iljEJss3WOwcsMkWy72Yn2zvjw7Gyxc+RXL7r8YKM6g= -github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260526195338-adcf8013a1b7/go.mod h1:vTFHTCbLui4Vn8fTmAadfE3rdnvfrDwOmMujmW857D0= +github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260602131523-5168ac1ba014 h1:4rxcbbe1qe1yR+HcclvOi/e0CFLcBLfx2fgiWxBMMZ4= +github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260602131523-5168ac1ba014/go.mod h1:vTFHTCbLui4Vn8fTmAadfE3rdnvfrDwOmMujmW857D0= github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20251002192024-d2ad9222409b h1:QuI6SmQFK/zyUlVWEf0GMkiUYBPY4lssn26nKSd/bOM= github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20251002192024-d2ad9222409b/go.mod h1:qSTSwX3cBP3FKQwQacdjArqv0g6QnukjV4XuzO6UyoY= github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260319180422-b5808c964785 h1:oli+2uLU6jcrJGCuYFqk3475hiwL17SWlITWLv+tx/w= From 99af42b0711664980506fdcd50354d8aaa490e71 Mon Sep 17 00:00:00 2001 From: Vladimir Shchukin Date: Thu, 4 Jun 2026 18:28:31 -0400 Subject: [PATCH 02/16] implement GetProgramAccounts --- chain_capabilities/solana/actions/actions.go | 38 +++++++++++++++++++- chain_capabilities/solana/go.mod | 4 +-- chain_capabilities/solana/go.sum | 10 +++--- 3 files changed, 45 insertions(+), 7 deletions(-) diff --git a/chain_capabilities/solana/actions/actions.go b/chain_capabilities/solana/actions/actions.go index 777fc65b7..a7f52cae9 100644 --- a/chain_capabilities/solana/actions/actions.go +++ b/chain_capabilities/solana/actions/actions.go @@ -10,8 +10,8 @@ import ( "github.com/smartcontractkit/capabilities/chain_capabilities/solana/metering" "github.com/smartcontractkit/capabilities/libs/chainconsensus" - commonMon "github.com/smartcontractkit/capabilities/libs/monitoring" ctypes "github.com/smartcontractkit/capabilities/libs/chainconsensus/types" + commonMon "github.com/smartcontractkit/capabilities/libs/monitoring" "github.com/smartcontractkit/chainlink-common/pkg/beholder" "github.com/smartcontractkit/chainlink-common/pkg/capabilities" @@ -371,6 +371,42 @@ func (s *Solana) GetTransaction( return responseAndMetadata, nil } +func (s *Solana) GetProgramAccounts( + ctx context.Context, + metadata capabilities.RequestMetadata, + input *solcap.GetProgramAccountsRequest) (*capabilities.ResponseAndMetadata[*solcap.GetProgramAccountsReply], caperrors.Error) { + if !s.readsEnabled { + return nil, caperrors.NewPublicSystemError(errors.New("reads are not available"), caperrors.Internal) + } + request, err := solcap.ConvertGetProgramAccountsRequestFromProto(input) + if err != nil { + return nil, NewUserError(fmt.Errorf("invalid request: %w", err)) + } + + lggr := s.messageBuilder.RequestLggr(s.lggr, monitoring.TelemetryContext{TsStart: time.Now().UnixMilli(), RequestMetadata: metadata}).With("request", request) + lggr.Debugw("Received GetProgramAccounts request") + cReq := ctypes.NewVolatileRequest(metadata.WorkflowExecutionID, metadata.ReferenceID, metering.GetResponseMetadata(metering.GetAccountInfo), func(ctx context.Context) (*solcap.GetProgramAccountsReply, uint64, error) { + rawResponse, err := s.SolanaService.GetProgramAccounts(ctx, *request) + if err != nil { + return nil, 0, err + } + + response, err := solcap.ConvertGetProgramAccountsReplyToProto(rawResponse) + if err != nil { + return nil, 0, caperrors.NewPublicSystemError(fmt.Errorf("failed to convert response to proto: %w", err), caperrors.Internal) + } + + return response, 0, nil + }, lggr) + responseAndMetadata, err := chainconsensus.ReadHashableRequestReport[*solcap.GetProgramAccountsReply](ctx, s.handler, cReq) + if err != nil { + return nil, getReadError(lggr, fmt.Errorf("failed to GetProgramAccounts: %w", err)) + } + + lggr.Debugw("Successfully handled GetProgramAccounts") + return responseAndMetadata, nil +} + func (s *Solana) initLimiters(limitsFactory limits.Factory) (err error) { // PLEX-1920 this is initial values taken from chainlink-solana/docs/forwarder. Can be tuned later s.reportSizeLimit, err = limits.MakeUpperBoundLimiter(limitsFactory, cresettings.Default.PerWorkflow.ChainWrite.Solana.ReportSizeLimit) diff --git a/chain_capabilities/solana/go.mod b/chain_capabilities/solana/go.mod index 0c5ac3b28..d5ae9a432 100644 --- a/chain_capabilities/solana/go.mod +++ b/chain_capabilities/solana/go.mod @@ -9,9 +9,9 @@ require ( github.com/smartcontractkit/capabilities/chain_capabilities/common v0.0.0-20260601161303-0b4b1ac1c3fb github.com/smartcontractkit/capabilities/libs v0.0.0-20260604135015-e9711af26f89 github.com/smartcontractkit/chain-selectors v1.0.100 - github.com/smartcontractkit/chainlink-common v0.11.2-0.20260604133138-930561c898fc + github.com/smartcontractkit/chainlink-common v0.11.2-0.20260604221656-22d6c395dad4 github.com/smartcontractkit/chainlink-framework/multinode v0.0.0-20260326180413-c69f27e37a13 - github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260602131523-5168ac1ba014 + github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260604171908-6734db2d444f github.com/smartcontractkit/chainlink-solana v1.1.2-0.20260420191419-ea62f88cbdb4 github.com/smartcontractkit/chainlink-solana/contracts v0.0.0-20260421131224-c46cbfe7bc6c github.com/smartcontractkit/libocr v0.0.0-20260403184524-b6409238958d diff --git a/chain_capabilities/solana/go.sum b/chain_capabilities/solana/go.sum index f02411002..26bda5400 100644 --- a/chain_capabilities/solana/go.sum +++ b/chain_capabilities/solana/go.sum @@ -629,8 +629,10 @@ github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20260310183131-8 github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20260310183131-8d0f0e383288/go.mod h1:jPHlo/IN2YAArI001JJixmm6ZHQwgnAVJXY8VBFiFTc= github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20260310183131-8d0f0e383288 h1:eEjTgIQn4RW0ZPRepUDYTdgGwaRCMawMwgXkHItUc9U= github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20260310183131-8d0f0e383288/go.mod h1:67YbnoglYD61Pz/jTVCgav9wFq7S35OU8UyQSvPllRw= -github.com/smartcontractkit/chainlink-common v0.11.2-0.20260604133138-930561c898fc h1:bW6w4k8BWz+2KF3vVM45IekL5Ul3yiNEBkeKHDYUOx0= -github.com/smartcontractkit/chainlink-common v0.11.2-0.20260604133138-930561c898fc/go.mod h1:zC5csAXmmn2FZbZ78Rrfc4AvmEKJzKQLMEY/w2SRwDo= +github.com/smartcontractkit/chainlink-common v0.11.2-0.20260604203529-08c3cd7dd3f4 h1:wIZl/b8B5eDt0YopOPz2yDzHlpcN+qLA0iW//v3+MHI= +github.com/smartcontractkit/chainlink-common v0.11.2-0.20260604203529-08c3cd7dd3f4/go.mod h1:fP9RqD25/gTx3XqRstN8o4lAI3jp42vwJBLRZwRoOOM= +github.com/smartcontractkit/chainlink-common v0.11.2-0.20260604221656-22d6c395dad4 h1:/gxsOr1d/I8Xx0qjZSQieiWMDr944IzsGHgKwmXFbRg= +github.com/smartcontractkit/chainlink-common v0.11.2-0.20260604221656-22d6c395dad4/go.mod h1:fP9RqD25/gTx3XqRstN8o4lAI3jp42vwJBLRZwRoOOM= github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.20260601211238-9f526774fef0 h1:NExKM/D0HneOq/N5LGTbkV4VOa0UHCvfTNEb4GqYpto= github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.20260601211238-9f526774fef0/go.mod h1:HmUyH2oD9m+GRpKq7q3vuRnm1F2Uczf/Nd1v3ipMSK8= github.com/smartcontractkit/chainlink-common/pkg/monitoring v0.0.0-20251215152504-b1e41f508340 h1:PsjEI+5jZIz9AS4eOsLS5VpSWJINf38clXV3wryPyMk= @@ -641,8 +643,8 @@ github.com/smartcontractkit/chainlink-framework/multinode v0.0.0-20260326180413- github.com/smartcontractkit/chainlink-framework/multinode v0.0.0-20260326180413-c69f27e37a13/go.mod h1:Y7h84PqCe/Vimf2h1Nc6tMiOJStDbtM33fEUeaaF5xk= github.com/smartcontractkit/chainlink-protos/billing/go v0.0.0-20251024234028-0988426d98f4 h1:GCzrxDWn3b7jFfEA+WiYRi8CKoegsayiDoJBCjYkneE= github.com/smartcontractkit/chainlink-protos/billing/go v0.0.0-20251024234028-0988426d98f4/go.mod h1:HHGeDUpAsPa0pmOx7wrByCitjQ0mbUxf0R9v+g67uCA= -github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260602131523-5168ac1ba014 h1:4rxcbbe1qe1yR+HcclvOi/e0CFLcBLfx2fgiWxBMMZ4= -github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260602131523-5168ac1ba014/go.mod h1:vTFHTCbLui4Vn8fTmAadfE3rdnvfrDwOmMujmW857D0= +github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260604171908-6734db2d444f h1:t+OoYaXLdH0WHK2pbWKjTSnSQa5JBQD1+gf0yISYfQk= +github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260604171908-6734db2d444f/go.mod h1:vTFHTCbLui4Vn8fTmAadfE3rdnvfrDwOmMujmW857D0= github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20251002192024-d2ad9222409b h1:QuI6SmQFK/zyUlVWEf0GMkiUYBPY4lssn26nKSd/bOM= github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20251002192024-d2ad9222409b/go.mod h1:qSTSwX3cBP3FKQwQacdjArqv0g6QnukjV4XuzO6UyoY= github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260319180422-b5808c964785 h1:oli+2uLU6jcrJGCuYFqk3475hiwL17SWlITWLv+tx/w= From 41df34ba7717f0e00086ba86bd0745aeac8aa221 Mon Sep 17 00:00:00 2001 From: Vladimir Shchukin Date: Thu, 4 Jun 2026 19:20:47 -0400 Subject: [PATCH 03/16] add unit tests for actions --- .../solana/actions/actions_test.go | 664 +++++++++++++++++- .../solana/actions/write_report_test.go | 8 +- .../solana/trigger/trigger_test.go | 15 +- 3 files changed, 675 insertions(+), 12 deletions(-) diff --git a/chain_capabilities/solana/actions/actions_test.go b/chain_capabilities/solana/actions/actions_test.go index 388e97e2d..9aa21301e 100644 --- a/chain_capabilities/solana/actions/actions_test.go +++ b/chain_capabilities/solana/actions/actions_test.go @@ -167,7 +167,7 @@ func newMockedSolana(t *testing.T, readsEnabled bool) *mockedSolana { lggr := logger.Test(t) service := &Solana{ readsEnabled: readsEnabled, - SolanaService: mockSolanaService, + SolanaService: mocks.WrapSolanaService(mockSolanaService), beholderProcessor: NopBeholderProcessor{}, messageBuilder: monitoring.NewMessageBuilder(types.ChainInfo{}, capabilities.CapabilityInfo{}, ""), chainSelector: 1, @@ -239,3 +239,665 @@ func runVolatileHashableHandle(ctx context.Context, req ctypes.Request) (<-chan ch <- reply return ch, nil } + +// runECHashableHandle simulates OCR consensus for ECHashableRequest (EventuallyConsistent). +func runECHashableHandle(ctx context.Context, req ctypes.Request) (<-chan ctypes.Reply, error) { + observableRequest, ok := req.(ctypes.ObservableRequest) + if !ok { + return nil, fmt.Errorf("request is not an ObservableRequest") + } + _ = observableRequest.CaptureObservation(ctx) + observation, err := observableRequest.GetOCRObservation() + if err != nil { + return nil, fmt.Errorf("failed to get OCR observation: %w", err) + } + if observation == nil { + ch := make(chan ctypes.Reply, 1) + ch <- ctypes.Reply{Err: fmt.Errorf("no observation captured")} + return ch, nil + } + + var reply ctypes.Reply + switch tObs := observation.Observation.(type) { + case *ctypes.RequestObservation_Hashable: + hashBytes := tObs.Hashable + if len(hashBytes) != ctypes.HashLength { + return nil, fmt.Errorf("unexpected hash length: got %d, want %d", len(hashBytes), ctypes.HashLength) + } + var reportData ctypes.Hash + copy(reportData[:], hashBytes) + reply = ctypes.Reply{Value: ctypes.NewHashableRequestReport(ocrtypes.ConfigDigest{}, 0, reportData, nil)} + case *ctypes.RequestObservation_Error: + reply = ctypes.Reply{Err: ctypes.ObservationError(tObs.Error).Err()} + default: + return nil, fmt.Errorf("unexpected observation type: %T", observation.Observation) + } + + ch := make(chan ctypes.Reply, 1) + ch <- reply + return ch, nil +} + +// runAggregatableHandle simulates OCR consensus for AggregatableRequest (Volatile Aggregatable). +// It passes the observed Decimal value straight through as the "aggregated" result. +func runAggregatableHandle(ctx context.Context, req ctypes.Request) (<-chan ctypes.Reply, error) { + observableRequest, ok := req.(ctypes.ObservableRequest) + if !ok { + return nil, fmt.Errorf("request is not an ObservableRequest") + } + _ = observableRequest.CaptureObservation(ctx) + observation, err := observableRequest.GetOCRObservation() + if err != nil { + return nil, fmt.Errorf("failed to get OCR observation: %w", err) + } + if observation == nil { + ch := make(chan ctypes.Reply, 1) + ch <- ctypes.Reply{Err: fmt.Errorf("no observation captured")} + return ch, nil + } + + var reply ctypes.Reply + switch tObs := observation.Observation.(type) { + case *ctypes.RequestObservation_Aggregatable: + if tObs.Aggregatable == nil { + return nil, fmt.Errorf("nil aggregatable observation") + } + reply = ctypes.Reply{Value: tObs.Aggregatable.Value} + case *ctypes.RequestObservation_Error: + reply = ctypes.Reply{Err: ctypes.ObservationError(tObs.Error).Err()} + default: + return nil, fmt.Errorf("unexpected observation type: %T", observation.Observation) + } + + ch := make(chan ctypes.Reply, 1) + ch <- reply + return ch, nil +} + +// validSig returns a valid 64-byte signature slice. +func validSig() []byte { + sig := make([]byte, soltypes.SignatureLength) + for i := range sig { + sig[i] = byte(i + 1) + } + return sig +} + +// ---- GetBalance ---- + +func TestGetBalance(t *testing.T) { + t.Parallel() + + t.Run("reads disabled", func(t *testing.T) { + t.Parallel() + helper := newMockedSolana(t, false) + _, err := helper.solana.GetBalance(t.Context(), capabilities.RequestMetadata{ + WorkflowExecutionID: "weid", ReferenceID: "ref", + }, &solcap.GetBalanceRequest{Addr: solana.NewWallet().PublicKey().Bytes()}) + require.Error(t, err) + require.Contains(t, err.Error(), "reads are not available") + }) + + t.Run("invalid addr", func(t *testing.T) { + t.Parallel() + helper := newMockedSolana(t, true) + _, err := helper.solana.GetBalance(t.Context(), capabilities.RequestMetadata{ + WorkflowExecutionID: "weid", ReferenceID: "ref", + }, &solcap.GetBalanceRequest{Addr: []byte{1, 2, 3}}) + require.Error(t, err) + var capErr caperrors.Error + require.True(t, errors.As(err, &capErr)) + require.Equal(t, caperrors.OriginUser, capErr.Origin()) + require.Contains(t, err.Error(), "invalid request") + }) + + t.Run("happy path", func(t *testing.T) { + t.Parallel() + helper := newMockedSolana(t, true) + key, err := solana.NewRandomPrivateKey() + require.NoError(t, err) + + helper.solanaService.EXPECT(). + GetBalance(mock.Anything, mock.MatchedBy(func(req soltypes.GetBalanceRequest) bool { + return req.Addr == soltypes.PublicKey(key.PublicKey()) + })). + Return(&soltypes.GetBalanceReply{Value: 1_000_000}, nil).Once() + + resp, err := helper.solana.GetBalance(t.Context(), capabilities.RequestMetadata{ + WorkflowExecutionID: "weid", ReferenceID: "ref", + }, &solcap.GetBalanceRequest{Addr: key.PublicKey().Bytes()}) + require.NoError(t, err) + require.NotNil(t, resp) + require.Equal(t, uint64(1_000_000), resp.Response.Value) + require.NotNil(t, resp.OCRAttestation) + }) + + t.Run("service error", func(t *testing.T) { + t.Parallel() + helper := newMockedSolana(t, true) + key, err := solana.NewRandomPrivateKey() + require.NoError(t, err) + svcErr := errors.New("rpc down") + + helper.solanaService.EXPECT(). + GetBalance(mock.Anything, mock.Anything). + Return((*soltypes.GetBalanceReply)(nil), svcErr).Once() + + _, err = helper.solana.GetBalance(t.Context(), capabilities.RequestMetadata{ + WorkflowExecutionID: "weid", ReferenceID: "ref", + }, &solcap.GetBalanceRequest{Addr: key.PublicKey().Bytes()}) + require.Error(t, err) + require.ErrorContains(t, err, svcErr.Error()) + }) +} + +// ---- GetMultipleAccountsWithOpts ---- + +func TestGetMultipleAccountsWithOpts(t *testing.T) { + t.Parallel() + + t.Run("reads disabled", func(t *testing.T) { + t.Parallel() + helper := newMockedSolana(t, false) + _, err := helper.solana.GetMultipleAccountsWithOpts(t.Context(), capabilities.RequestMetadata{ + WorkflowExecutionID: "weid", ReferenceID: "ref", + }, &solcap.GetMultipleAccountsWithOptsRequest{ + Accounts: [][]byte{solana.NewWallet().PublicKey().Bytes()}, + }) + require.Error(t, err) + require.Contains(t, err.Error(), "reads are not available") + }) + + t.Run("invalid account key", func(t *testing.T) { + t.Parallel() + helper := newMockedSolana(t, true) + _, err := helper.solana.GetMultipleAccountsWithOpts(t.Context(), capabilities.RequestMetadata{ + WorkflowExecutionID: "weid", ReferenceID: "ref", + }, &solcap.GetMultipleAccountsWithOptsRequest{ + Accounts: [][]byte{{1, 2, 3}}, + }) + require.Error(t, err) + var capErr caperrors.Error + require.True(t, errors.As(err, &capErr)) + require.Equal(t, caperrors.OriginUser, capErr.Origin()) + }) + + t.Run("happy path", func(t *testing.T) { + t.Parallel() + helper := newMockedSolana(t, true) + key, err := solana.NewRandomPrivateKey() + require.NoError(t, err) + + serviceReply := &soltypes.GetMultipleAccountsReply{ + RPCContext: soltypes.RPCContext{Slot: 77}, + Value: []*soltypes.Account{{Lamports: 500}}, + } + helper.solanaService.EXPECT(). + GetMultipleAccountsWithOpts(mock.Anything, mock.Anything). + Return(serviceReply, nil).Once() + + resp, err := helper.solana.GetMultipleAccountsWithOpts(t.Context(), capabilities.RequestMetadata{ + WorkflowExecutionID: "weid", ReferenceID: "ref", + }, &solcap.GetMultipleAccountsWithOptsRequest{ + Accounts: [][]byte{key.PublicKey().Bytes()}, + }) + require.NoError(t, err) + require.NotNil(t, resp) + require.Len(t, resp.Response.Value, 1) + require.NotNil(t, resp.OCRAttestation) + }) + + t.Run("service error", func(t *testing.T) { + t.Parallel() + helper := newMockedSolana(t, true) + key, err := solana.NewRandomPrivateKey() + require.NoError(t, err) + svcErr := errors.New("rpc timeout") + + helper.solanaService.EXPECT(). + GetMultipleAccountsWithOpts(mock.Anything, mock.Anything). + Return((*soltypes.GetMultipleAccountsReply)(nil), svcErr).Once() + + _, err = helper.solana.GetMultipleAccountsWithOpts(t.Context(), capabilities.RequestMetadata{ + WorkflowExecutionID: "weid", ReferenceID: "ref", + }, &solcap.GetMultipleAccountsWithOptsRequest{ + Accounts: [][]byte{key.PublicKey().Bytes()}, + }) + require.Error(t, err) + require.ErrorContains(t, err, svcErr.Error()) + }) +} + +// ---- GetProgramAccounts ---- + +func TestGetProgramAccounts(t *testing.T) { + t.Parallel() + + t.Run("reads disabled", func(t *testing.T) { + t.Parallel() + helper := newMockedSolana(t, false) + _, err := helper.solana.GetProgramAccounts(t.Context(), capabilities.RequestMetadata{ + WorkflowExecutionID: "weid", ReferenceID: "ref", + }, &solcap.GetProgramAccountsRequest{Program: solana.NewWallet().PublicKey().Bytes()}) + require.Error(t, err) + require.Contains(t, err.Error(), "reads are not available") + }) + + t.Run("invalid program key", func(t *testing.T) { + t.Parallel() + helper := newMockedSolana(t, true) + _, err := helper.solana.GetProgramAccounts(t.Context(), capabilities.RequestMetadata{ + WorkflowExecutionID: "weid", ReferenceID: "ref", + }, &solcap.GetProgramAccountsRequest{Program: []byte{1, 2, 3}}) + require.Error(t, err) + var capErr caperrors.Error + require.True(t, errors.As(err, &capErr)) + require.Equal(t, caperrors.OriginUser, capErr.Origin()) + require.Contains(t, err.Error(), "invalid request") + }) + + t.Run("nil filter entry", func(t *testing.T) { + t.Parallel() + helper := newMockedSolana(t, true) + key, err := solana.NewRandomPrivateKey() + require.NoError(t, err) + _, err = helper.solana.GetProgramAccounts(t.Context(), capabilities.RequestMetadata{ + WorkflowExecutionID: "weid", ReferenceID: "ref", + }, &solcap.GetProgramAccountsRequest{ + Program: key.PublicKey().Bytes(), + Opts: &solcap.GetProgramAccountsOpts{Filters: []*solcap.RPCFilter{nil}}, + }) + require.Error(t, err) + var capErr caperrors.Error + require.True(t, errors.As(err, &capErr)) + require.Equal(t, caperrors.OriginUser, capErr.Origin()) + }) + + t.Run("happy path", func(t *testing.T) { + t.Parallel() + helper := newMockedSolana(t, true) + key, err := solana.NewRandomPrivateKey() + require.NoError(t, err) + + serviceReply := &soltypes.GetProgramAccountsReply{ + Value: []*soltypes.KeyedAccount{ + {Pubkey: soltypes.PublicKey(key.PublicKey())}, + }, + } + helper.solanaService.EXPECT(). + GetProgramAccounts(mock.Anything, mock.MatchedBy(func(req soltypes.GetProgramAccountsRequest) bool { + return req.Program == soltypes.PublicKey(key.PublicKey()) + })). + Return(serviceReply, nil).Once() + + resp, err := helper.solana.GetProgramAccounts(t.Context(), capabilities.RequestMetadata{ + WorkflowExecutionID: "weid", ReferenceID: "ref", + }, &solcap.GetProgramAccountsRequest{Program: key.PublicKey().Bytes()}) + require.NoError(t, err) + require.NotNil(t, resp) + require.Len(t, resp.Response.Value, 1) + require.NotNil(t, resp.OCRAttestation) + }) + + t.Run("service error", func(t *testing.T) { + t.Parallel() + helper := newMockedSolana(t, true) + key, err := solana.NewRandomPrivateKey() + require.NoError(t, err) + svcErr := errors.New("rpc unavailable") + + helper.solanaService.EXPECT(). + GetProgramAccounts(mock.Anything, mock.Anything). + Return((*soltypes.GetProgramAccountsReply)(nil), svcErr).Once() + + _, err = helper.solana.GetProgramAccounts(t.Context(), capabilities.RequestMetadata{ + WorkflowExecutionID: "weid", ReferenceID: "ref", + }, &solcap.GetProgramAccountsRequest{Program: key.PublicKey().Bytes()}) + require.Error(t, err) + require.ErrorContains(t, err, svcErr.Error()) + }) +} + +// ---- GetTransaction ---- + +func TestGetTransaction(t *testing.T) { + t.Parallel() + + t.Run("reads disabled", func(t *testing.T) { + t.Parallel() + helper := newMockedSolana(t, false) + helper.solana.handler = testConsensusHandler{handle: runECHashableHandle} + _, err := helper.solana.GetTransaction(t.Context(), capabilities.RequestMetadata{ + WorkflowExecutionID: "weid", ReferenceID: "ref", + }, &solcap.GetTransactionRequest{Signature: validSig()}) + require.Error(t, err) + require.Contains(t, err.Error(), "reads are not available") + }) + + t.Run("invalid signature", func(t *testing.T) { + t.Parallel() + helper := newMockedSolana(t, true) + helper.solana.handler = testConsensusHandler{handle: runECHashableHandle} + _, err := helper.solana.GetTransaction(t.Context(), capabilities.RequestMetadata{ + WorkflowExecutionID: "weid", ReferenceID: "ref", + }, &solcap.GetTransactionRequest{Signature: []byte{1, 2, 3}}) + require.Error(t, err) + var capErr caperrors.Error + require.True(t, errors.As(err, &capErr)) + require.Equal(t, caperrors.OriginUser, capErr.Origin()) + }) + + t.Run("happy path", func(t *testing.T) { + t.Parallel() + helper := newMockedSolana(t, true) + helper.solana.handler = testConsensusHandler{handle: runECHashableHandle} + + serviceReply := &soltypes.GetTransactionReply{Slot: 99} + helper.solanaService.EXPECT(). + GetTransaction(mock.Anything, mock.Anything). + Return(serviceReply, nil).Once() + + resp, err := helper.solana.GetTransaction(t.Context(), capabilities.RequestMetadata{ + WorkflowExecutionID: "weid", ReferenceID: "ref", + }, &solcap.GetTransactionRequest{Signature: validSig()}) + require.NoError(t, err) + require.NotNil(t, resp) + require.Equal(t, uint64(99), resp.Response.Slot) + require.NotNil(t, resp.OCRAttestation) + }) + + t.Run("service error", func(t *testing.T) { + t.Parallel() + helper := newMockedSolana(t, true) + helper.solana.handler = testConsensusHandler{handle: runECHashableHandle} + svcErr := errors.New("tx not found") + + helper.solanaService.EXPECT(). + GetTransaction(mock.Anything, mock.Anything). + Return((*soltypes.GetTransactionReply)(nil), svcErr).Once() + + _, err := helper.solana.GetTransaction(t.Context(), capabilities.RequestMetadata{ + WorkflowExecutionID: "weid", ReferenceID: "ref", + }, &solcap.GetTransactionRequest{Signature: validSig()}) + require.Error(t, err) + require.ErrorContains(t, err, svcErr.Error()) + }) +} + +// ---- GetSignatureStatuses ---- + +func TestGetSignatureStatuses(t *testing.T) { + t.Parallel() + + t.Run("reads disabled", func(t *testing.T) { + t.Parallel() + helper := newMockedSolana(t, false) + helper.solana.handler = testConsensusHandler{handle: runECHashableHandle} + _, err := helper.solana.GetSignatureStatuses(t.Context(), capabilities.RequestMetadata{ + WorkflowExecutionID: "weid", ReferenceID: "ref", + }, &solcap.GetSignatureStatusesRequest{Sigs: [][]byte{validSig()}}) + require.Error(t, err) + require.Contains(t, err.Error(), "reads are not available") + }) + + t.Run("invalid signature", func(t *testing.T) { + t.Parallel() + helper := newMockedSolana(t, true) + helper.solana.handler = testConsensusHandler{handle: runECHashableHandle} + _, err := helper.solana.GetSignatureStatuses(t.Context(), capabilities.RequestMetadata{ + WorkflowExecutionID: "weid", ReferenceID: "ref", + }, &solcap.GetSignatureStatusesRequest{Sigs: [][]byte{{1, 2, 3}}}) + require.Error(t, err) + var capErr caperrors.Error + require.True(t, errors.As(err, &capErr)) + require.Equal(t, caperrors.OriginUser, capErr.Origin()) + }) + + t.Run("happy path", func(t *testing.T) { + t.Parallel() + helper := newMockedSolana(t, true) + helper.solana.handler = testConsensusHandler{handle: runECHashableHandle} + + conf := uint64(10) + serviceReply := &soltypes.GetSignatureStatusesReply{ + Results: []soltypes.GetSignatureStatusesResult{ + {Slot: 42, Confirmations: &conf, ConfirmationStatus: soltypes.ConfirmationStatusConfirmed}, + }, + } + helper.solanaService.EXPECT(). + GetSignatureStatuses(mock.Anything, mock.Anything). + Return(serviceReply, nil).Once() + + resp, err := helper.solana.GetSignatureStatuses(t.Context(), capabilities.RequestMetadata{ + WorkflowExecutionID: "weid", ReferenceID: "ref", + }, &solcap.GetSignatureStatusesRequest{Sigs: [][]byte{validSig()}}) + require.NoError(t, err) + require.NotNil(t, resp) + require.Len(t, resp.Response.Results, 1) + require.Equal(t, uint64(42), resp.Response.Results[0].Slot) + require.NotNil(t, resp.OCRAttestation) + }) + + t.Run("service error", func(t *testing.T) { + t.Parallel() + helper := newMockedSolana(t, true) + helper.solana.handler = testConsensusHandler{handle: runECHashableHandle} + svcErr := errors.New("sig lookup failed") + + helper.solanaService.EXPECT(). + GetSignatureStatuses(mock.Anything, mock.Anything). + Return((*soltypes.GetSignatureStatusesReply)(nil), svcErr).Once() + + _, err := helper.solana.GetSignatureStatuses(t.Context(), capabilities.RequestMetadata{ + WorkflowExecutionID: "weid", ReferenceID: "ref", + }, &solcap.GetSignatureStatusesRequest{Sigs: [][]byte{validSig()}}) + require.Error(t, err) + require.ErrorContains(t, err, svcErr.Error()) + }) +} + +// ---- GetBlock ---- + +func TestGetBlock(t *testing.T) { + t.Parallel() + + t.Run("reads disabled", func(t *testing.T) { + t.Parallel() + helper := newMockedSolana(t, false) + helper.solana.handler = testConsensusHandler{handle: runECHashableHandle} + _, err := helper.solana.GetBlock(t.Context(), capabilities.RequestMetadata{ + WorkflowExecutionID: "weid", ReferenceID: "ref", + }, &solcap.GetBlockRequest{Slot: 1}) + require.Error(t, err) + require.Contains(t, err.Error(), "reads are not available") + }) + + t.Run("invalid commitment", func(t *testing.T) { + t.Parallel() + helper := newMockedSolana(t, true) + helper.solana.handler = testConsensusHandler{handle: runECHashableHandle} + _, err := helper.solana.GetBlock(t.Context(), capabilities.RequestMetadata{ + WorkflowExecutionID: "weid", ReferenceID: "ref", + }, &solcap.GetBlockRequest{ + Slot: 1, + Opts: &solcap.GetBlockOpts{Commitment: solcap.CommitmentType(999)}, + }) + require.Error(t, err) + var capErr caperrors.Error + require.True(t, errors.As(err, &capErr)) + require.Equal(t, caperrors.OriginUser, capErr.Origin()) + }) + + t.Run("happy path", func(t *testing.T) { + t.Parallel() + helper := newMockedSolana(t, true) + helper.solana.handler = testConsensusHandler{handle: runECHashableHandle} + + serviceReply := &soltypes.GetBlockReply{ParentSlot: 41} + helper.solanaService.EXPECT(). + GetBlock(mock.Anything, mock.MatchedBy(func(req soltypes.GetBlockRequest) bool { + return req.Slot == 42 + })). + Return(serviceReply, nil).Once() + + resp, err := helper.solana.GetBlock(t.Context(), capabilities.RequestMetadata{ + WorkflowExecutionID: "weid", ReferenceID: "ref", + }, &solcap.GetBlockRequest{Slot: 42}) + require.NoError(t, err) + require.NotNil(t, resp) + require.Equal(t, uint64(41), resp.Response.ParentSlot) + require.NotNil(t, resp.OCRAttestation) + }) + + t.Run("service error", func(t *testing.T) { + t.Parallel() + helper := newMockedSolana(t, true) + helper.solana.handler = testConsensusHandler{handle: runECHashableHandle} + svcErr := errors.New("block not available") + + helper.solanaService.EXPECT(). + GetBlock(mock.Anything, mock.Anything). + Return((*soltypes.GetBlockReply)(nil), svcErr).Once() + + _, err := helper.solana.GetBlock(t.Context(), capabilities.RequestMetadata{ + WorkflowExecutionID: "weid", ReferenceID: "ref", + }, &solcap.GetBlockRequest{Slot: 42}) + require.Error(t, err) + require.ErrorContains(t, err, svcErr.Error()) + }) +} + +// ---- GetSlotHeight ---- + +func TestGetSlotHeight(t *testing.T) { + t.Parallel() + + t.Run("reads disabled", func(t *testing.T) { + t.Parallel() + helper := newMockedSolana(t, false) + helper.solana.handler = testConsensusHandler{handle: runAggregatableHandle} + _, err := helper.solana.GetSlotHeight(t.Context(), capabilities.RequestMetadata{ + WorkflowExecutionID: "weid", ReferenceID: "ref", + }, &solcap.GetSlotHeightRequest{}) + require.Error(t, err) + require.Contains(t, err.Error(), "reads are not available") + }) + + t.Run("invalid commitment", func(t *testing.T) { + t.Parallel() + helper := newMockedSolana(t, true) + helper.solana.handler = testConsensusHandler{handle: runAggregatableHandle} + _, err := helper.solana.GetSlotHeight(t.Context(), capabilities.RequestMetadata{ + WorkflowExecutionID: "weid", ReferenceID: "ref", + }, &solcap.GetSlotHeightRequest{Commitment: solcap.CommitmentType(999)}) + require.Error(t, err) + var capErr caperrors.Error + require.True(t, errors.As(err, &capErr)) + require.Equal(t, caperrors.OriginUser, capErr.Origin()) + }) + + t.Run("happy path", func(t *testing.T) { + t.Parallel() + helper := newMockedSolana(t, true) + helper.solana.handler = testConsensusHandler{handle: runAggregatableHandle} + + const height uint64 = 500_000 + helper.solanaService.EXPECT(). + GetSlotHeight(mock.Anything, mock.Anything). + Return(&soltypes.GetSlotHeightReply{Height: height}, nil).Once() + + resp, err := helper.solana.GetSlotHeight(t.Context(), capabilities.RequestMetadata{ + WorkflowExecutionID: "weid", ReferenceID: "ref", + }, &solcap.GetSlotHeightRequest{Commitment: solcap.CommitmentType_COMMITMENT_TYPE_CONFIRMED}) + require.NoError(t, err) + require.NotNil(t, resp) + require.Equal(t, height, resp.Response.Height) + }) + + t.Run("service error", func(t *testing.T) { + t.Parallel() + helper := newMockedSolana(t, true) + helper.solana.handler = testConsensusHandler{handle: runAggregatableHandle} + svcErr := errors.New("slot unavailable") + + helper.solanaService.EXPECT(). + GetSlotHeight(mock.Anything, mock.Anything). + Return((*soltypes.GetSlotHeightReply)(nil), svcErr).Once() + + _, err := helper.solana.GetSlotHeight(t.Context(), capabilities.RequestMetadata{ + WorkflowExecutionID: "weid", ReferenceID: "ref", + }, &solcap.GetSlotHeightRequest{}) + require.Error(t, err) + require.ErrorContains(t, err, svcErr.Error()) + }) +} + +// ---- GetFeeForMessage ---- + +func TestGetFeeForMessage(t *testing.T) { + t.Parallel() + + t.Run("reads disabled", func(t *testing.T) { + t.Parallel() + helper := newMockedSolana(t, false) + helper.solana.handler = testConsensusHandler{handle: runAggregatableHandle} + _, err := helper.solana.GetFeeForMessage(t.Context(), capabilities.RequestMetadata{ + WorkflowExecutionID: "weid", ReferenceID: "ref", + }, &solcap.GetFeeForMessageRequest{Message: "someMsg"}) + require.Error(t, err) + require.Contains(t, err.Error(), "reads are not available") + }) + + t.Run("invalid commitment", func(t *testing.T) { + t.Parallel() + helper := newMockedSolana(t, true) + helper.solana.handler = testConsensusHandler{handle: runAggregatableHandle} + _, err := helper.solana.GetFeeForMessage(t.Context(), capabilities.RequestMetadata{ + WorkflowExecutionID: "weid", ReferenceID: "ref", + }, &solcap.GetFeeForMessageRequest{ + Message: "someMsg", + Commitment: solcap.CommitmentType(999), + }) + require.Error(t, err) + var capErr caperrors.Error + require.True(t, errors.As(err, &capErr)) + require.Equal(t, caperrors.OriginUser, capErr.Origin()) + }) + + t.Run("happy path", func(t *testing.T) { + t.Parallel() + helper := newMockedSolana(t, true) + helper.solana.handler = testConsensusHandler{handle: runAggregatableHandle} + + const fee uint64 = 5000 + helper.solanaService.EXPECT(). + GetFeeForMessage(mock.Anything, mock.MatchedBy(func(req soltypes.GetFeeForMessageRequest) bool { + return req.Message == "someMsg" + })). + Return(&soltypes.GetFeeForMessageReply{Fee: fee}, nil).Once() + + resp, err := helper.solana.GetFeeForMessage(t.Context(), capabilities.RequestMetadata{ + WorkflowExecutionID: "weid", ReferenceID: "ref", + }, &solcap.GetFeeForMessageRequest{Message: "someMsg"}) + require.NoError(t, err) + require.NotNil(t, resp) + require.Equal(t, fee, resp.Response.Fee) + }) + + t.Run("service error", func(t *testing.T) { + t.Parallel() + helper := newMockedSolana(t, true) + helper.solana.handler = testConsensusHandler{handle: runAggregatableHandle} + svcErr := errors.New("fee estimation failed") + + helper.solanaService.EXPECT(). + GetFeeForMessage(mock.Anything, mock.Anything). + Return((*soltypes.GetFeeForMessageReply)(nil), svcErr).Once() + + _, err := helper.solana.GetFeeForMessage(t.Context(), capabilities.RequestMetadata{ + WorkflowExecutionID: "weid", ReferenceID: "ref", + }, &solcap.GetFeeForMessageRequest{Message: "someMsg"}) + require.Error(t, err) + require.ErrorContains(t, err, svcErr.Error()) + }) +} diff --git a/chain_capabilities/solana/actions/write_report_test.go b/chain_capabilities/solana/actions/write_report_test.go index 597120d55..3899fb7d4 100644 --- a/chain_capabilities/solana/actions/write_report_test.go +++ b/chain_capabilities/solana/actions/write_report_test.go @@ -324,7 +324,7 @@ func createMocksAndCapability(t *testing.T, lggr logger.Logger) *testHelper { mockTrInfo := NewTransmissionInfoProvider_mock(t) mockClient := NewCREForwarderClient_mock(t) service := &Solana{ - SolanaService: mockSolanaService, + SolanaService: mocks.WrapSolanaService(mockSolanaService), forwarderClient: mockClient, transmissionInfoProvider: mockTrInfo, beholderProcessor: NopBeholderProcessor{}, @@ -736,7 +736,7 @@ func TestGetFee(t *testing.T) { mockSolanaService := mocks.NewSolanaService(t) wr := &WriteReport{ - SolanaService: mockSolanaService, + SolanaService: mocks.WrapSolanaService(mockSolanaService), lggr: testLogger, } @@ -756,7 +756,7 @@ func TestGetFee(t *testing.T) { mockSolanaService := mocks.NewSolanaService(t) wr := &WriteReport{ - SolanaService: mockSolanaService, + SolanaService: mocks.WrapSolanaService(mockSolanaService), lggr: testLogger, } @@ -776,7 +776,7 @@ func TestGetFee(t *testing.T) { mockSolanaService := mocks.NewSolanaService(t) wr := &WriteReport{ - SolanaService: mockSolanaService, + SolanaService: mocks.WrapSolanaService(mockSolanaService), lggr: testLogger, } diff --git a/chain_capabilities/solana/trigger/trigger_test.go b/chain_capabilities/solana/trigger/trigger_test.go index 31fa3f02c..178c307de 100644 --- a/chain_capabilities/solana/trigger/trigger_test.go +++ b/chain_capabilities/solana/trigger/trigger_test.go @@ -118,7 +118,7 @@ func setupTest(t *testing.T) (*SolanaLogTriggerService, *mocks.SolanaService) { lggr := logger.Test(t) opts := LogTriggerServiceOpts{ - SolanaService: mockSolanaService, + SolanaService: mocks.WrapSolanaService(mockSolanaService), Logger: lggr, BeholderProcessor: NopBeholderProcessor{}, MessageBuilder: monitoring.NewMessageBuilder(types.ChainInfo{}, capabilities.CapabilityInfo{}, ""), @@ -624,7 +624,7 @@ func TestStartPolling(t *testing.T) { store := NewSolanaLogTriggerStore() opts := LogTriggerServiceOpts{ - SolanaService: mockSolanaService, + SolanaService: mocks.WrapSolanaService(mockSolanaService), Logger: logger.Nop(), BeholderProcessor: NopBeholderProcessor{}, MessageBuilder: monitoring.NewMessageBuilder(types.ChainInfo{}, capabilities.CapabilityInfo{}, ""), @@ -733,7 +733,7 @@ func TestStartPolling(t *testing.T) { // Create service with very small buffer opts := LogTriggerServiceOpts{ - SolanaService: mockSolanaService, + SolanaService: mocks.WrapSolanaService(mockSolanaService), Logger: logger.Test(t), Triggers: store, LogTriggerPollInterval: 1 * time.Millisecond, @@ -786,7 +786,7 @@ func TestStartPolling(t *testing.T) { store := NewSolanaLogTriggerStore() opts := LogTriggerServiceOpts{ - SolanaService: mockSolanaService, + SolanaService: mocks.WrapSolanaService(mockSolanaService), Logger: logger.Test(t), Triggers: store, LogTriggerPollInterval: 10 * time.Millisecond, @@ -844,7 +844,7 @@ func TestStartPolling(t *testing.T) { store := NewSolanaLogTriggerStore() opts := LogTriggerServiceOpts{ - SolanaService: mockSolanaService, + SolanaService: mocks.WrapSolanaService(mockSolanaService), Logger: logger.Test(t), Triggers: store, LogTriggerPollInterval: 5 * time.Millisecond, @@ -1125,11 +1125,12 @@ func TestSolanaLogTriggerService_NewLogTriggerService(t *testing.T) { t.Run("respects provided values", func(t *testing.T) { mockService := mocks.NewSolanaService(t) + wrappedService := mocks.WrapSolanaService(mockService) store := NewSolanaLogTriggerStore() lggr := logger.Test(t) opts := LogTriggerServiceOpts{ - SolanaService: mockService, + SolanaService: wrappedService, Logger: lggr, Triggers: store, LogTriggerPollInterval: 5 * time.Second, @@ -1144,7 +1145,7 @@ func TestSolanaLogTriggerService_NewLogTriggerService(t *testing.T) { require.NoError(t, err) require.NotNil(t, service) - assert.Equal(t, mockService, service.SolanaService) + assert.Equal(t, wrappedService, service.SolanaService) assert.Equal(t, store, service.triggers) assert.Equal(t, 5*time.Second, service.logTriggerPollInterval) assert.Equal(t, uint64(2000), service.logTriggerSendChannelBufferSize) From 80a3d2cb0a25e4d5585ab9d5b9a319aa52c4abf3 Mon Sep 17 00:00:00 2001 From: Vladimir Shchukin Date: Thu, 4 Jun 2026 21:22:03 -0400 Subject: [PATCH 04/16] bump common --- chain_capabilities/common/go.mod | 4 ++-- chain_capabilities/common/go.sum | 8 ++++---- chain_capabilities/solana/go.mod | 2 +- chain_capabilities/solana/go.sum | 6 ++---- 4 files changed, 9 insertions(+), 11 deletions(-) diff --git a/chain_capabilities/common/go.mod b/chain_capabilities/common/go.mod index 3460480ae..9e1d7302e 100644 --- a/chain_capabilities/common/go.mod +++ b/chain_capabilities/common/go.mod @@ -5,7 +5,7 @@ go 1.26.2 require ( github.com/jpillora/backoff v1.0.0 github.com/smartcontractkit/capabilities/libs v0.0.0-20260602154159-3bc5aa37c661 - github.com/smartcontractkit/chainlink-common v0.11.2-0.20260601211238-9f526774fef0 + github.com/smartcontractkit/chainlink-common v0.11.2-0.20260604234426-27a72f7f8cf8 github.com/smartcontractkit/libocr v0.0.0-20250912173940-f3ab0246e23d github.com/stretchr/testify v1.11.1 go.opentelemetry.io/otel v1.43.0 @@ -55,7 +55,7 @@ require ( github.com/shopspring/decimal v1.4.0 // indirect github.com/smartcontractkit/chain-selectors v1.0.100 // indirect github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.20260601211238-9f526774fef0 // indirect - github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260526195338-adcf8013a1b7 // indirect + github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260604171908-6734db2d444f // indirect github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20251002192024-d2ad9222409b // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect diff --git a/chain_capabilities/common/go.sum b/chain_capabilities/common/go.sum index 3e5dfb939..06e6ed197 100644 --- a/chain_capabilities/common/go.sum +++ b/chain_capabilities/common/go.sum @@ -94,12 +94,12 @@ github.com/smartcontractkit/capabilities/libs v0.0.0-20260602154159-3bc5aa37c661 github.com/smartcontractkit/capabilities/libs v0.0.0-20260602154159-3bc5aa37c661/go.mod h1:LS7F8U2YZNc0Vt8f6SVWUUigGLxdxZMpyC7VCcUTagg= github.com/smartcontractkit/chain-selectors v1.0.100 h1:wpiSpmI/eFjY+wx/nPr5VuNF4hki0prIBMKEaQWn3g4= github.com/smartcontractkit/chain-selectors v1.0.100/go.mod h1:qy7whtgG5g+7z0jt0nRyii9bLND9m15NZTzuQPkMZ5w= -github.com/smartcontractkit/chainlink-common v0.11.2-0.20260601211238-9f526774fef0 h1:ekpMT6wV+caBWnaBGUD/j1eoal+DhNLq7jv1hFf/nyU= -github.com/smartcontractkit/chainlink-common v0.11.2-0.20260601211238-9f526774fef0/go.mod h1:6jgqiFXFJHqjkvFFmuf8gvoUFa6Ygx/D1tKnIL+CCF8= +github.com/smartcontractkit/chainlink-common v0.11.2-0.20260604234426-27a72f7f8cf8 h1:pm4xOepOj4uGYKqzVJxIi3MnLleYKTuLcxBNW51s9jI= +github.com/smartcontractkit/chainlink-common v0.11.2-0.20260604234426-27a72f7f8cf8/go.mod h1:fP9RqD25/gTx3XqRstN8o4lAI3jp42vwJBLRZwRoOOM= github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.20260601211238-9f526774fef0 h1:NExKM/D0HneOq/N5LGTbkV4VOa0UHCvfTNEb4GqYpto= github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.20260601211238-9f526774fef0/go.mod h1:HmUyH2oD9m+GRpKq7q3vuRnm1F2Uczf/Nd1v3ipMSK8= -github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260526195338-adcf8013a1b7 h1:iljEJss3WOwcsMkWy72Yn2zvjw7Gyxc+RXL7r8YKM6g= -github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260526195338-adcf8013a1b7/go.mod h1:vTFHTCbLui4Vn8fTmAadfE3rdnvfrDwOmMujmW857D0= +github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260604171908-6734db2d444f h1:t+OoYaXLdH0WHK2pbWKjTSnSQa5JBQD1+gf0yISYfQk= +github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260604171908-6734db2d444f/go.mod h1:vTFHTCbLui4Vn8fTmAadfE3rdnvfrDwOmMujmW857D0= github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20251002192024-d2ad9222409b h1:QuI6SmQFK/zyUlVWEf0GMkiUYBPY4lssn26nKSd/bOM= github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20251002192024-d2ad9222409b/go.mod h1:qSTSwX3cBP3FKQwQacdjArqv0g6QnukjV4XuzO6UyoY= github.com/smartcontractkit/libocr v0.0.0-20250912173940-f3ab0246e23d h1:LokA9PoCNb8mm8mDT52c3RECPMRsGz1eCQORq+J3n74= diff --git a/chain_capabilities/solana/go.mod b/chain_capabilities/solana/go.mod index d5ae9a432..258f78189 100644 --- a/chain_capabilities/solana/go.mod +++ b/chain_capabilities/solana/go.mod @@ -9,7 +9,7 @@ require ( github.com/smartcontractkit/capabilities/chain_capabilities/common v0.0.0-20260601161303-0b4b1ac1c3fb github.com/smartcontractkit/capabilities/libs v0.0.0-20260604135015-e9711af26f89 github.com/smartcontractkit/chain-selectors v1.0.100 - github.com/smartcontractkit/chainlink-common v0.11.2-0.20260604221656-22d6c395dad4 + github.com/smartcontractkit/chainlink-common v0.11.2-0.20260604234426-27a72f7f8cf8 github.com/smartcontractkit/chainlink-framework/multinode v0.0.0-20260326180413-c69f27e37a13 github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260604171908-6734db2d444f github.com/smartcontractkit/chainlink-solana v1.1.2-0.20260420191419-ea62f88cbdb4 diff --git a/chain_capabilities/solana/go.sum b/chain_capabilities/solana/go.sum index 26bda5400..59fc86299 100644 --- a/chain_capabilities/solana/go.sum +++ b/chain_capabilities/solana/go.sum @@ -629,10 +629,8 @@ github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20260310183131-8 github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20260310183131-8d0f0e383288/go.mod h1:jPHlo/IN2YAArI001JJixmm6ZHQwgnAVJXY8VBFiFTc= github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20260310183131-8d0f0e383288 h1:eEjTgIQn4RW0ZPRepUDYTdgGwaRCMawMwgXkHItUc9U= github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20260310183131-8d0f0e383288/go.mod h1:67YbnoglYD61Pz/jTVCgav9wFq7S35OU8UyQSvPllRw= -github.com/smartcontractkit/chainlink-common v0.11.2-0.20260604203529-08c3cd7dd3f4 h1:wIZl/b8B5eDt0YopOPz2yDzHlpcN+qLA0iW//v3+MHI= -github.com/smartcontractkit/chainlink-common v0.11.2-0.20260604203529-08c3cd7dd3f4/go.mod h1:fP9RqD25/gTx3XqRstN8o4lAI3jp42vwJBLRZwRoOOM= -github.com/smartcontractkit/chainlink-common v0.11.2-0.20260604221656-22d6c395dad4 h1:/gxsOr1d/I8Xx0qjZSQieiWMDr944IzsGHgKwmXFbRg= -github.com/smartcontractkit/chainlink-common v0.11.2-0.20260604221656-22d6c395dad4/go.mod h1:fP9RqD25/gTx3XqRstN8o4lAI3jp42vwJBLRZwRoOOM= +github.com/smartcontractkit/chainlink-common v0.11.2-0.20260604234426-27a72f7f8cf8 h1:pm4xOepOj4uGYKqzVJxIi3MnLleYKTuLcxBNW51s9jI= +github.com/smartcontractkit/chainlink-common v0.11.2-0.20260604234426-27a72f7f8cf8/go.mod h1:fP9RqD25/gTx3XqRstN8o4lAI3jp42vwJBLRZwRoOOM= github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.20260601211238-9f526774fef0 h1:NExKM/D0HneOq/N5LGTbkV4VOa0UHCvfTNEb4GqYpto= github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.20260601211238-9f526774fef0/go.mod h1:HmUyH2oD9m+GRpKq7q3vuRnm1F2Uczf/Nd1v3ipMSK8= github.com/smartcontractkit/chainlink-common/pkg/monitoring v0.0.0-20251215152504-b1e41f508340 h1:PsjEI+5jZIz9AS4eOsLS5VpSWJINf38clXV3wryPyMk= From b0899ed5d590f8e10d0ea3862aa22f4a53736379 Mon Sep 17 00:00:00 2001 From: Vladimir Shchukin Date: Thu, 4 Jun 2026 23:28:04 -0400 Subject: [PATCH 05/16] add sorting for GetProgramAccounts --- chain_capabilities/solana/actions/actions.go | 9 ++++ .../solana/actions/actions_test.go | 46 +++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/chain_capabilities/solana/actions/actions.go b/chain_capabilities/solana/actions/actions.go index a7f52cae9..2221c2edb 100644 --- a/chain_capabilities/solana/actions/actions.go +++ b/chain_capabilities/solana/actions/actions.go @@ -1,10 +1,12 @@ package actions import ( + "bytes" "context" "errors" "fmt" "math/big" + "slices" "strings" "time" @@ -22,6 +24,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/settings/cresettings" "github.com/smartcontractkit/chainlink-common/pkg/settings/limits" "github.com/smartcontractkit/chainlink-common/pkg/types" + soltypes "github.com/smartcontractkit/chainlink-common/pkg/types/chains/solana" "github.com/smartcontractkit/chainlink-framework/multinode" valuespb "github.com/smartcontractkit/chainlink-protos/cre/go/values/pb" @@ -391,6 +394,12 @@ func (s *Solana) GetProgramAccounts( return nil, 0, err } + // getProgramAccounts does not guarantee ordering across RPC nodes. + // Sort by pubkey so all nodes produce an identical hash. + slices.SortFunc(rawResponse.Value, func(a, b *soltypes.KeyedAccount) int { + return bytes.Compare(a.Pubkey[:], b.Pubkey[:]) + }) + response, err := solcap.ConvertGetProgramAccountsReplyToProto(rawResponse) if err != nil { return nil, 0, caperrors.NewPublicSystemError(fmt.Errorf("failed to convert response to proto: %w", err), caperrors.Internal) diff --git a/chain_capabilities/solana/actions/actions_test.go b/chain_capabilities/solana/actions/actions_test.go index 9aa21301e..a8730bb7c 100644 --- a/chain_capabilities/solana/actions/actions_test.go +++ b/chain_capabilities/solana/actions/actions_test.go @@ -539,6 +539,52 @@ func TestGetProgramAccounts(t *testing.T) { require.NotNil(t, resp.OCRAttestation) }) + t.Run("accounts sorted by pubkey for deterministic hashing", func(t *testing.T) { + t.Parallel() + helper := newMockedSolana(t, true) + program, err := solana.NewRandomPrivateKey() + require.NoError(t, err) + + // Build three keys whose raw bytes are deliberately out of order. + highKey, err := solana.NewRandomPrivateKey() + require.NoError(t, err) + lowKey, err := solana.NewRandomPrivateKey() + require.NoError(t, err) + midKey, err := solana.NewRandomPrivateKey() + require.NoError(t, err) + + // Force a deterministic ordering: low < mid < high by assigning first byte. + var low, mid, high soltypes.PublicKey + lowPK, midPK, highPK := lowKey.PublicKey(), midKey.PublicKey(), highKey.PublicKey() + low[0], mid[0], high[0] = 0x10, 0x50, 0xFF + copy(low[1:], lowPK[1:]) + copy(mid[1:], midPK[1:]) + copy(high[1:], highPK[1:]) + + // RPC returns accounts in reverse order (high → mid → low). + serviceReply := &soltypes.GetProgramAccountsReply{ + Value: []*soltypes.KeyedAccount{ + {Pubkey: high}, + {Pubkey: mid}, + {Pubkey: low}, + }, + } + helper.solanaService.EXPECT(). + GetProgramAccounts(mock.Anything, mock.Anything). + Return(serviceReply, nil).Once() + + resp, err := helper.solana.GetProgramAccounts(t.Context(), capabilities.RequestMetadata{ + WorkflowExecutionID: "weid", ReferenceID: "ref", + }, &solcap.GetProgramAccountsRequest{Program: program.PublicKey().Bytes()}) + require.NoError(t, err) + require.Len(t, resp.Response.Value, 3) + + // After sorting the response must be low → mid → high. + require.Equal(t, low[:], resp.Response.Value[0].Pubkey) + require.Equal(t, mid[:], resp.Response.Value[1].Pubkey) + require.Equal(t, high[:], resp.Response.Value[2].Pubkey) + }) + t.Run("service error", func(t *testing.T) { t.Parallel() helper := newMockedSolana(t, true) From 84fe65248f6643fcc8f2b928963bc454b2f684be Mon Sep 17 00:00:00 2001 From: Vladimir Shchukin Date: Sat, 6 Jun 2026 13:04:08 -0400 Subject: [PATCH 06/16] run tidy --- integration_tests/go.mod | 4 ++-- integration_tests/go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/integration_tests/go.mod b/integration_tests/go.mod index 584a95c78..c3d5818e5 100644 --- a/integration_tests/go.mod +++ b/integration_tests/go.mod @@ -26,10 +26,10 @@ require ( github.com/smartcontractkit/capabilities/http_trigger v0.0.0-00010101000000-000000000000 github.com/smartcontractkit/capabilities/loadtestwritetarget v0.0.0-00010101000000-000000000000 github.com/smartcontractkit/chain-selectors v1.0.101 - github.com/smartcontractkit/chainlink-common v0.11.2-0.20260603103051-071d30a40591 + github.com/smartcontractkit/chainlink-common v0.11.2-0.20260604234426-27a72f7f8cf8 github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260512150409-b4068bf735e6 github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20260119171452-39c98c3b33cd - github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260602131523-5168ac1ba014 + github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260604171908-6734db2d444f github.com/smartcontractkit/chainlink-protos/workflows/go v0.0.0-20260528173149-f5b8336b19d9 github.com/smartcontractkit/chainlink/v2 v2.29.1-cre-beta.0.0.20260512171733-704bf3201286 github.com/smartcontractkit/cre-sdk-go v1.5.0 diff --git a/integration_tests/go.sum b/integration_tests/go.sum index f982f85a0..43bf6270b 100644 --- a/integration_tests/go.sum +++ b/integration_tests/go.sum @@ -1162,8 +1162,8 @@ github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20260 github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20260415165642-49f23e4d76cc/go.mod h1:67YbnoglYD61Pz/jTVCgav9wFq7S35OU8UyQSvPllRw= github.com/smartcontractkit/chainlink-ccv v0.0.2-0.20260428133800-3b1484e8b1fd h1:IMopuENFVS63AerRELdfWo6o60UNUidcldJOxJLmk24= github.com/smartcontractkit/chainlink-ccv v0.0.2-0.20260428133800-3b1484e8b1fd/go.mod h1:SBN8Urnh5sQvrQRbSo1Nr8coWatHg8LZoPw3R/42sho= -github.com/smartcontractkit/chainlink-common v0.11.2-0.20260603103051-071d30a40591 h1:KQHgXMoNJcSLesRwVc2gJ2Wmc/AbUv3lcdDrXKd+4Eo= -github.com/smartcontractkit/chainlink-common v0.11.2-0.20260603103051-071d30a40591/go.mod h1:zC5csAXmmn2FZbZ78Rrfc4AvmEKJzKQLMEY/w2SRwDo= +github.com/smartcontractkit/chainlink-common v0.11.2-0.20260604234426-27a72f7f8cf8 h1:pm4xOepOj4uGYKqzVJxIi3MnLleYKTuLcxBNW51s9jI= +github.com/smartcontractkit/chainlink-common v0.11.2-0.20260604234426-27a72f7f8cf8/go.mod h1:fP9RqD25/gTx3XqRstN8o4lAI3jp42vwJBLRZwRoOOM= github.com/smartcontractkit/chainlink-common/keystore v1.1.1-0.20260529092756-a94bc8ce96d6 h1:fWsYxxj35fp1/6YZngoTsOTMLqDie4N5X0osAOdhUTE= github.com/smartcontractkit/chainlink-common/keystore v1.1.1-0.20260529092756-a94bc8ce96d6/go.mod h1:6JexOOhPhknQ0QMuppFIlOpm6wCp54yZMxai+tWugwY= github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.20260601211238-9f526774fef0 h1:NExKM/D0HneOq/N5LGTbkV4VOa0UHCvfTNEb4GqYpto= @@ -1196,8 +1196,8 @@ github.com/smartcontractkit/chainlink-protos/chainlink-ccv/message-discovery v0. github.com/smartcontractkit/chainlink-protos/chainlink-ccv/message-discovery v0.0.0-20251211142334-5c3421fe2c8d/go.mod h1:ATjAPIVJibHRcIfiG47rEQkUIOoYa6KDvWj3zwCAw6g= github.com/smartcontractkit/chainlink-protos/chainlink-ccv/verifier v0.0.0-20251211142334-5c3421fe2c8d h1:AJy55QJ/pBhXkZjc7N+ATnWfxrcjq9BI9DmdtdjwDUQ= github.com/smartcontractkit/chainlink-protos/chainlink-ccv/verifier v0.0.0-20251211142334-5c3421fe2c8d/go.mod h1:5JdppgngCOUS76p61zCinSCgOhPeYQ+OcDUuome5THQ= -github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260602131523-5168ac1ba014 h1:4rxcbbe1qe1yR+HcclvOi/e0CFLcBLfx2fgiWxBMMZ4= -github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260602131523-5168ac1ba014/go.mod h1:vTFHTCbLui4Vn8fTmAadfE3rdnvfrDwOmMujmW857D0= +github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260604171908-6734db2d444f h1:t+OoYaXLdH0WHK2pbWKjTSnSQa5JBQD1+gf0yISYfQk= +github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260604171908-6734db2d444f/go.mod h1:vTFHTCbLui4Vn8fTmAadfE3rdnvfrDwOmMujmW857D0= github.com/smartcontractkit/chainlink-protos/data-feeds v0.1.1-0.20260501174546-2e8846986b36 h1:SG+wAsNyAcA6Kk19ljuxi3HK9Ll2lpHik8OKoY4x7A0= github.com/smartcontractkit/chainlink-protos/data-feeds v0.1.1-0.20260501174546-2e8846986b36/go.mod h1:vL1bDgPSJjV0EqHYs4dDlR+EEE0cJchgvGLYXhwIjXY= github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20251002192024-d2ad9222409b h1:QuI6SmQFK/zyUlVWEf0GMkiUYBPY4lssn26nKSd/bOM= From 5e6e50c0c668e29e8f2e6f5373ee4fbf3cd1373e Mon Sep 17 00:00:00 2001 From: Vladimir Shchukin Date: Sat, 6 Jun 2026 15:07:41 -0400 Subject: [PATCH 07/16] mark external requests --- chain_capabilities/solana/actions/actions.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/chain_capabilities/solana/actions/actions.go b/chain_capabilities/solana/actions/actions.go index 2221c2edb..d7fbe6074 100644 --- a/chain_capabilities/solana/actions/actions.go +++ b/chain_capabilities/solana/actions/actions.go @@ -88,7 +88,7 @@ func (s *Solana) GetAccountInfoWithOpts( if err != nil { return nil, NewUserError(fmt.Errorf("invalid request: %w", err)) } - + request.IsExternal = true lggr := s.messageBuilder.RequestLggr(s.lggr, monitoring.TelemetryContext{TsStart: time.Now().UnixMilli(), RequestMetadata: metadata}).With("request", request) lggr.Debugw("Received GetAccountInfoWithOpts request") cReq := ctypes.NewVolatileRequest(metadata.WorkflowExecutionID, metadata.ReferenceID, metering.GetResponseMetadata(metering.GetAccountInfo), func(ctx context.Context) (*solcap.GetAccountInfoWithOptsReply, uint64, error) { @@ -237,7 +237,7 @@ func (s *Solana) GetMultipleAccountsWithOpts( if err != nil { return nil, NewUserError(fmt.Errorf("invalid request: %w", err)) } - + request.IsExternal = true lggr := s.messageBuilder.RequestLggr(s.lggr, monitoring.TelemetryContext{TsStart: time.Now().UnixMilli(), RequestMetadata: metadata}).With("request", request) lggr.Debugw("Received GetMultipleAccountsWithOpts request") cReq := ctypes.NewVolatileRequest(metadata.WorkflowExecutionID, metadata.ReferenceID, metering.GetResponseMetadata(metering.GetAccountInfo), func(ctx context.Context) (*solcap.GetMultipleAccountsWithOptsReply, uint64, error) { @@ -349,7 +349,7 @@ func (s *Solana) GetTransaction( if err != nil { return nil, NewUserError(fmt.Errorf("invalid request: %w", err)) } - + request.IsExternal = true lggr := s.messageBuilder.RequestLggr(s.lggr, monitoring.TelemetryContext{TsStart: time.Now().UnixMilli(), RequestMetadata: metadata}).With("request", request) lggr.Debugw("Received GetTransaction request") cReq := ctypes.NewECHashableRequest(metadata.WorkflowExecutionID, metadata.ReferenceID, metering.GetResponseMetadata(metering.GetAccountInfo), func(ctx context.Context) (*solcap.GetTransactionReply, error) { @@ -385,7 +385,7 @@ func (s *Solana) GetProgramAccounts( if err != nil { return nil, NewUserError(fmt.Errorf("invalid request: %w", err)) } - + request.IsExternal = true lggr := s.messageBuilder.RequestLggr(s.lggr, monitoring.TelemetryContext{TsStart: time.Now().UnixMilli(), RequestMetadata: metadata}).With("request", request) lggr.Debugw("Received GetProgramAccounts request") cReq := ctypes.NewVolatileRequest(metadata.WorkflowExecutionID, metadata.ReferenceID, metering.GetResponseMetadata(metering.GetAccountInfo), func(ctx context.Context) (*solcap.GetProgramAccountsReply, uint64, error) { From 8d58b7c9c3021f5df65e217d2b6cfe903175db94 Mon Sep 17 00:00:00 2001 From: Vladimir Shchukin Date: Sat, 6 Jun 2026 15:15:53 -0400 Subject: [PATCH 08/16] update cl solana ref --- chain_capabilities/solana/go.mod | 6 +++--- chain_capabilities/solana/go.sum | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/chain_capabilities/solana/go.mod b/chain_capabilities/solana/go.mod index 258f78189..f57c17702 100644 --- a/chain_capabilities/solana/go.mod +++ b/chain_capabilities/solana/go.mod @@ -10,9 +10,9 @@ require ( github.com/smartcontractkit/capabilities/libs v0.0.0-20260604135015-e9711af26f89 github.com/smartcontractkit/chain-selectors v1.0.100 github.com/smartcontractkit/chainlink-common v0.11.2-0.20260604234426-27a72f7f8cf8 - github.com/smartcontractkit/chainlink-framework/multinode v0.0.0-20260326180413-c69f27e37a13 + github.com/smartcontractkit/chainlink-framework/multinode v0.0.0-20260521164805-26d78d5e1243 github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260604171908-6734db2d444f - github.com/smartcontractkit/chainlink-solana v1.1.2-0.20260420191419-ea62f88cbdb4 + github.com/smartcontractkit/chainlink-solana v1.3.1-0.20260605011329-f035a0f22700 github.com/smartcontractkit/chainlink-solana/contracts v0.0.0-20260421131224-c46cbfe7bc6c github.com/smartcontractkit/libocr v0.0.0-20260403184524-b6409238958d github.com/stretchr/testify v1.11.1 @@ -132,7 +132,7 @@ require ( github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20260310183131-8d0f0e383288 // indirect github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20260310183131-8d0f0e383288 // indirect github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.20260601211238-9f526774fef0 // indirect - github.com/smartcontractkit/chainlink-framework/metrics v0.0.0-20251210101658-1c5c8e4c4f15 // indirect + github.com/smartcontractkit/chainlink-framework/metrics v0.0.0-20260521164805-26d78d5e1243 // indirect github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20251002192024-d2ad9222409b // indirect github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260319180422-b5808c964785 // indirect github.com/smartcontractkit/freeport v0.1.3-0.20250828155247-add56fa28aad // indirect diff --git a/chain_capabilities/solana/go.sum b/chain_capabilities/solana/go.sum index 59fc86299..5cc680859 100644 --- a/chain_capabilities/solana/go.sum +++ b/chain_capabilities/solana/go.sum @@ -635,10 +635,10 @@ github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.202606012 github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.20260601211238-9f526774fef0/go.mod h1:HmUyH2oD9m+GRpKq7q3vuRnm1F2Uczf/Nd1v3ipMSK8= github.com/smartcontractkit/chainlink-common/pkg/monitoring v0.0.0-20251215152504-b1e41f508340 h1:PsjEI+5jZIz9AS4eOsLS5VpSWJINf38clXV3wryPyMk= github.com/smartcontractkit/chainlink-common/pkg/monitoring v0.0.0-20251215152504-b1e41f508340/go.mod h1:P/0OSXUlFaxxD4B/P6HWbxYtIRmmWGDJAvanq19879c= -github.com/smartcontractkit/chainlink-framework/metrics v0.0.0-20251210101658-1c5c8e4c4f15 h1:IXF7+k8I1YY/yvXC1wnS3FAAggtCy6ByEQ9hv/F2FvQ= -github.com/smartcontractkit/chainlink-framework/metrics v0.0.0-20251210101658-1c5c8e4c4f15/go.mod h1:HG/aei0MgBOpsyRLexdKGtOUO8yjSJO3iUu0Uu8KBm4= -github.com/smartcontractkit/chainlink-framework/multinode v0.0.0-20260326180413-c69f27e37a13 h1:3KLLkTCIAy9CvT35Ey0k6pcWX/u+qsm3Y/58TI5VSAg= -github.com/smartcontractkit/chainlink-framework/multinode v0.0.0-20260326180413-c69f27e37a13/go.mod h1:Y7h84PqCe/Vimf2h1Nc6tMiOJStDbtM33fEUeaaF5xk= +github.com/smartcontractkit/chainlink-framework/metrics v0.0.0-20260521164805-26d78d5e1243 h1:vaFBupfFfImQgqOeuC7Muk2GflbYP6Gpi0Y/TLroFU8= +github.com/smartcontractkit/chainlink-framework/metrics v0.0.0-20260521164805-26d78d5e1243/go.mod h1:HG/aei0MgBOpsyRLexdKGtOUO8yjSJO3iUu0Uu8KBm4= +github.com/smartcontractkit/chainlink-framework/multinode v0.0.0-20260521164805-26d78d5e1243 h1:71PGTkjdFZ0JrloEC2Fs8eHl1b1gmUuH+bq7q23usKk= +github.com/smartcontractkit/chainlink-framework/multinode v0.0.0-20260521164805-26d78d5e1243/go.mod h1:7ketk4ischPQW/JQgmyHz6zdzLUJv1VC29SiSgosydQ= github.com/smartcontractkit/chainlink-protos/billing/go v0.0.0-20251024234028-0988426d98f4 h1:GCzrxDWn3b7jFfEA+WiYRi8CKoegsayiDoJBCjYkneE= github.com/smartcontractkit/chainlink-protos/billing/go v0.0.0-20251024234028-0988426d98f4/go.mod h1:HHGeDUpAsPa0pmOx7wrByCitjQ0mbUxf0R9v+g67uCA= github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260604171908-6734db2d444f h1:t+OoYaXLdH0WHK2pbWKjTSnSQa5JBQD1+gf0yISYfQk= @@ -649,8 +649,8 @@ github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260319180422 github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260319180422-b5808c964785/go.mod h1:dkR2uYg9XYJuT1JASkPzWE51jjFkVb86P7a/yXe5/GM= github.com/smartcontractkit/chainlink-protos/workflows/go v0.0.0-20260528173149-f5b8336b19d9 h1:LQy2j2+TdKLSWsUTUYuqmQPn8kjqCLjGI3ZJYGtDc08= github.com/smartcontractkit/chainlink-protos/workflows/go v0.0.0-20260528173149-f5b8336b19d9/go.mod h1:GTpDgyK0OObf7jpch6p8N281KxN92wbB8serZhU9yRc= -github.com/smartcontractkit/chainlink-solana v1.1.2-0.20260420191419-ea62f88cbdb4 h1:WaQzRyMCmi7gddZraPtHSqCjZ5dnVYFYXqEtq2DsQjw= -github.com/smartcontractkit/chainlink-solana v1.1.2-0.20260420191419-ea62f88cbdb4/go.mod h1:sUsEwLtVPBlz0wPcysaolS+HVj9cOAt4jYhwE6J8dXg= +github.com/smartcontractkit/chainlink-solana v1.3.1-0.20260605011329-f035a0f22700 h1:5bAR287CiKHaD8ixk1WUo8prH51pFJiKvvv3JkvSYuw= +github.com/smartcontractkit/chainlink-solana v1.3.1-0.20260605011329-f035a0f22700/go.mod h1:JPSK+7ejSuLnxwrJpywm9umUQKXuWm35uk3NIbwFSs0= github.com/smartcontractkit/chainlink-solana/contracts v0.0.0-20260421131224-c46cbfe7bc6c h1:Hn/80PyYFrQhRlNSaq9HY4cjc/7AuP9zyWLle22t34A= github.com/smartcontractkit/chainlink-solana/contracts v0.0.0-20260421131224-c46cbfe7bc6c/go.mod h1:C5pZsbYX3qkhZTYWr1aYJi9QMfonFAun+Jl1npQ7UJA= github.com/smartcontractkit/freeport v0.1.3-0.20250828155247-add56fa28aad h1:lgHxTHuzJIF3Vj6LSMOnjhqKgRqYW+0MV2SExtCYL1Q= From 5f32c22457f93bfe45e0e15ca794cd55a64a3fa4 Mon Sep 17 00:00:00 2001 From: Vladimir Shchukin Date: Sat, 6 Jun 2026 15:26:12 -0400 Subject: [PATCH 09/16] bump sol ref --- chain_capabilities/solana/go.mod | 2 +- chain_capabilities/solana/go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/chain_capabilities/solana/go.mod b/chain_capabilities/solana/go.mod index f57c17702..4a4be65ea 100644 --- a/chain_capabilities/solana/go.mod +++ b/chain_capabilities/solana/go.mod @@ -12,7 +12,7 @@ require ( github.com/smartcontractkit/chainlink-common v0.11.2-0.20260604234426-27a72f7f8cf8 github.com/smartcontractkit/chainlink-framework/multinode v0.0.0-20260521164805-26d78d5e1243 github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260604171908-6734db2d444f - github.com/smartcontractkit/chainlink-solana v1.3.1-0.20260605011329-f035a0f22700 + github.com/smartcontractkit/chainlink-solana v1.3.1-0.20260605150919-6d1f761cc654 github.com/smartcontractkit/chainlink-solana/contracts v0.0.0-20260421131224-c46cbfe7bc6c github.com/smartcontractkit/libocr v0.0.0-20260403184524-b6409238958d github.com/stretchr/testify v1.11.1 diff --git a/chain_capabilities/solana/go.sum b/chain_capabilities/solana/go.sum index 5cc680859..a0dad1001 100644 --- a/chain_capabilities/solana/go.sum +++ b/chain_capabilities/solana/go.sum @@ -649,8 +649,8 @@ github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260319180422 github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260319180422-b5808c964785/go.mod h1:dkR2uYg9XYJuT1JASkPzWE51jjFkVb86P7a/yXe5/GM= github.com/smartcontractkit/chainlink-protos/workflows/go v0.0.0-20260528173149-f5b8336b19d9 h1:LQy2j2+TdKLSWsUTUYuqmQPn8kjqCLjGI3ZJYGtDc08= github.com/smartcontractkit/chainlink-protos/workflows/go v0.0.0-20260528173149-f5b8336b19d9/go.mod h1:GTpDgyK0OObf7jpch6p8N281KxN92wbB8serZhU9yRc= -github.com/smartcontractkit/chainlink-solana v1.3.1-0.20260605011329-f035a0f22700 h1:5bAR287CiKHaD8ixk1WUo8prH51pFJiKvvv3JkvSYuw= -github.com/smartcontractkit/chainlink-solana v1.3.1-0.20260605011329-f035a0f22700/go.mod h1:JPSK+7ejSuLnxwrJpywm9umUQKXuWm35uk3NIbwFSs0= +github.com/smartcontractkit/chainlink-solana v1.3.1-0.20260605150919-6d1f761cc654 h1:uPWmfISAfBB59gJ/refKlB5FxatF4xk0rKUSW0q+754= +github.com/smartcontractkit/chainlink-solana v1.3.1-0.20260605150919-6d1f761cc654/go.mod h1:JPSK+7ejSuLnxwrJpywm9umUQKXuWm35uk3NIbwFSs0= github.com/smartcontractkit/chainlink-solana/contracts v0.0.0-20260421131224-c46cbfe7bc6c h1:Hn/80PyYFrQhRlNSaq9HY4cjc/7AuP9zyWLle22t34A= github.com/smartcontractkit/chainlink-solana/contracts v0.0.0-20260421131224-c46cbfe7bc6c/go.mod h1:C5pZsbYX3qkhZTYWr1aYJi9QMfonFAun+Jl1npQ7UJA= github.com/smartcontractkit/freeport v0.1.3-0.20250828155247-add56fa28aad h1:lgHxTHuzJIF3Vj6LSMOnjhqKgRqYW+0MV2SExtCYL1Q= From 8e4433c2a87126df3b66c6a0f52eb563dadd5b88 Mon Sep 17 00:00:00 2001 From: Vladimir Shchukin Date: Sun, 7 Jun 2026 12:06:11 -0400 Subject: [PATCH 10/16] Implement monitoring context --- chain_capabilities/solana/actions/actions.go | 14 +++++++++++ chain_capabilities/solana/go.mod | 2 +- chain_capabilities/solana/go.sum | 4 +-- chain_capabilities/solana/main.go | 1 + .../solana/monitoring/messages.go | 25 +++++++++++++++++++ 5 files changed, 43 insertions(+), 3 deletions(-) diff --git a/chain_capabilities/solana/actions/actions.go b/chain_capabilities/solana/actions/actions.go index d7fbe6074..a40cc8860 100644 --- a/chain_capabilities/solana/actions/actions.go +++ b/chain_capabilities/solana/actions/actions.go @@ -19,6 +19,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/capabilities" caperrors "github.com/smartcontractkit/chainlink-common/pkg/capabilities/errors" solcap "github.com/smartcontractkit/chainlink-common/pkg/capabilities/v2/chain-capabilities/solana" + capmon "github.com/smartcontractkit/chainlink-common/pkg/capabilities/v2/monitoring" commoncfg "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/settings/cresettings" @@ -416,6 +417,19 @@ func (s *Solana) GetProgramAccounts( return responseAndMetadata, nil } +func (s *Solana) MonitoringContext() capmon.MonitoringContext { + return capmon.MonitoringContext{ + Logger: s.lggr, + Processor: s.beholderProcessor, + ExecCtx: func(metadata capabilities.RequestMetadata, tsStart time.Time) *capmon.ExecutionContext { + return s.messageBuilder.BuildV2ExecutionContext(monitoring.TelemetryContext{ + TsStart: tsStart.UnixMilli(), + RequestMetadata: metadata, + }) + }, + } +} + func (s *Solana) initLimiters(limitsFactory limits.Factory) (err error) { // PLEX-1920 this is initial values taken from chainlink-solana/docs/forwarder. Can be tuned later s.reportSizeLimit, err = limits.MakeUpperBoundLimiter(limitsFactory, cresettings.Default.PerWorkflow.ChainWrite.Solana.ReportSizeLimit) diff --git a/chain_capabilities/solana/go.mod b/chain_capabilities/solana/go.mod index 4a4be65ea..b60e91d85 100644 --- a/chain_capabilities/solana/go.mod +++ b/chain_capabilities/solana/go.mod @@ -9,7 +9,7 @@ require ( github.com/smartcontractkit/capabilities/chain_capabilities/common v0.0.0-20260601161303-0b4b1ac1c3fb github.com/smartcontractkit/capabilities/libs v0.0.0-20260604135015-e9711af26f89 github.com/smartcontractkit/chain-selectors v1.0.100 - github.com/smartcontractkit/chainlink-common v0.11.2-0.20260604234426-27a72f7f8cf8 + github.com/smartcontractkit/chainlink-common v0.11.2-0.20260607151512-4747f7356241 github.com/smartcontractkit/chainlink-framework/multinode v0.0.0-20260521164805-26d78d5e1243 github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260604171908-6734db2d444f github.com/smartcontractkit/chainlink-solana v1.3.1-0.20260605150919-6d1f761cc654 diff --git a/chain_capabilities/solana/go.sum b/chain_capabilities/solana/go.sum index a0dad1001..3d47ad9c1 100644 --- a/chain_capabilities/solana/go.sum +++ b/chain_capabilities/solana/go.sum @@ -629,8 +629,8 @@ github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20260310183131-8 github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20260310183131-8d0f0e383288/go.mod h1:jPHlo/IN2YAArI001JJixmm6ZHQwgnAVJXY8VBFiFTc= github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20260310183131-8d0f0e383288 h1:eEjTgIQn4RW0ZPRepUDYTdgGwaRCMawMwgXkHItUc9U= github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20260310183131-8d0f0e383288/go.mod h1:67YbnoglYD61Pz/jTVCgav9wFq7S35OU8UyQSvPllRw= -github.com/smartcontractkit/chainlink-common v0.11.2-0.20260604234426-27a72f7f8cf8 h1:pm4xOepOj4uGYKqzVJxIi3MnLleYKTuLcxBNW51s9jI= -github.com/smartcontractkit/chainlink-common v0.11.2-0.20260604234426-27a72f7f8cf8/go.mod h1:fP9RqD25/gTx3XqRstN8o4lAI3jp42vwJBLRZwRoOOM= +github.com/smartcontractkit/chainlink-common v0.11.2-0.20260607151512-4747f7356241 h1:CxI0uK1QQZt2hXvtmTDkGxHZE6J+SQ13YFu6uGr0wJE= +github.com/smartcontractkit/chainlink-common v0.11.2-0.20260607151512-4747f7356241/go.mod h1:fP9RqD25/gTx3XqRstN8o4lAI3jp42vwJBLRZwRoOOM= github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.20260601211238-9f526774fef0 h1:NExKM/D0HneOq/N5LGTbkV4VOa0UHCvfTNEb4GqYpto= github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.20260601211238-9f526774fef0/go.mod h1:HmUyH2oD9m+GRpKq7q3vuRnm1F2Uczf/Nd1v3ipMSK8= github.com/smartcontractkit/chainlink-common/pkg/monitoring v0.0.0-20251215152504-b1e41f508340 h1:PsjEI+5jZIz9AS4eOsLS5VpSWJINf38clXV3wryPyMk= diff --git a/chain_capabilities/solana/main.go b/chain_capabilities/solana/main.go index 290b8465f..810da37f6 100644 --- a/chain_capabilities/solana/main.go +++ b/chain_capabilities/solana/main.go @@ -125,6 +125,7 @@ func (c *capabilityGRPCService) Description() string { func (c *capabilityGRPCService) Ready() error { return nil } + func (c *capabilityGRPCService) Initialise(ctx context.Context, dependencies core.StandardCapabilitiesDependencies) error { c.lggr.Infof("Initialising %s", CapabilityName) diff --git a/chain_capabilities/solana/monitoring/messages.go b/chain_capabilities/solana/monitoring/messages.go index fe1fb1b3a..3fe10d86a 100644 --- a/chain_capabilities/solana/monitoring/messages.go +++ b/chain_capabilities/solana/monitoring/messages.go @@ -12,6 +12,7 @@ import ( "github.com/mr-tron/base58" "github.com/smartcontractkit/chainlink-common/pkg/capabilities" + capmon "github.com/smartcontractkit/chainlink-common/pkg/capabilities/v2/monitoring" solanacappb "github.com/smartcontractkit/chainlink-common/pkg/capabilities/v2/chain-capabilities/solana" "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink-common/pkg/types/chains/solana" @@ -36,6 +37,30 @@ func NewMessageBuilder(chainInfo types.ChainInfo, capInfo capabilities.Capabilit } } +// BuildV2ExecutionContext builds the v2 monitoring ExecutionContext used by the +// generated server (--with-monitoring) action lifecycle events. +func (m *MessageBuilder) BuildV2ExecutionContext(tc TelemetryContext) *capmon.ExecutionContext { + ec := m.BuildExecutionContext(tc) + return &capmon.ExecutionContext{ + MetaSourceId: ec.MetaSourceId, + MetaChainFamilyName: ec.MetaChainFamilyName, + MetaChainId: ec.MetaChainId, + MetaNetworkName: ec.MetaNetworkName, + MetaNetworkNameFull: ec.MetaNetworkNameFull, + MetaWorkflowId: ec.MetaWorkflowId, + MetaWorkflowOwner: ec.MetaWorkflowOwner, + MetaWorkflowExecutionId: ec.MetaWorkflowExecutionId, + MetaWorkflowName: ec.MetaWorkflowName, + MetaWorkflowDonId: ec.MetaWorkflowDonId, + MetaWorkflowDonConfigVersion: ec.MetaWorkflowDonConfigVersion, + MetaReferenceId: ec.MetaReferenceId, + MetaCapabilityType: ec.MetaCapabilityType, + MetaCapabilityId: ec.MetaCapabilityId, + MetaCapabilityTimestampStart: ec.MetaCapabilityTimestampStart, + MetaCapabilityTimestampEmit: ec.MetaCapabilityTimestampEmit, + } +} + func (m *MessageBuilder) BuildWriteReportTxFeeCalculationError(tc TelemetryContext, req *solcap.WriteReportRequest, signature solgo.Signature, cause string) ErrorMessage { summary := "Failed to calculate transaction fee" if !signature.IsZero() { From aa4e0dbfaaaef39df43cbfdbd1231b9c1f12660c Mon Sep 17 00:00:00 2001 From: Vladimir Shchukin Date: Sun, 7 Jun 2026 14:52:39 -0400 Subject: [PATCH 11/16] bump common --- chain_capabilities/solana/go.mod | 2 +- chain_capabilities/solana/go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/chain_capabilities/solana/go.mod b/chain_capabilities/solana/go.mod index b60e91d85..29f4eaa6e 100644 --- a/chain_capabilities/solana/go.mod +++ b/chain_capabilities/solana/go.mod @@ -9,7 +9,7 @@ require ( github.com/smartcontractkit/capabilities/chain_capabilities/common v0.0.0-20260601161303-0b4b1ac1c3fb github.com/smartcontractkit/capabilities/libs v0.0.0-20260604135015-e9711af26f89 github.com/smartcontractkit/chain-selectors v1.0.100 - github.com/smartcontractkit/chainlink-common v0.11.2-0.20260607151512-4747f7356241 + github.com/smartcontractkit/chainlink-common v0.11.2-0.20260607185208-f07e2a300359 github.com/smartcontractkit/chainlink-framework/multinode v0.0.0-20260521164805-26d78d5e1243 github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260604171908-6734db2d444f github.com/smartcontractkit/chainlink-solana v1.3.1-0.20260605150919-6d1f761cc654 diff --git a/chain_capabilities/solana/go.sum b/chain_capabilities/solana/go.sum index 3d47ad9c1..e5886d1bf 100644 --- a/chain_capabilities/solana/go.sum +++ b/chain_capabilities/solana/go.sum @@ -629,8 +629,8 @@ github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20260310183131-8 github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20260310183131-8d0f0e383288/go.mod h1:jPHlo/IN2YAArI001JJixmm6ZHQwgnAVJXY8VBFiFTc= github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20260310183131-8d0f0e383288 h1:eEjTgIQn4RW0ZPRepUDYTdgGwaRCMawMwgXkHItUc9U= github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20260310183131-8d0f0e383288/go.mod h1:67YbnoglYD61Pz/jTVCgav9wFq7S35OU8UyQSvPllRw= -github.com/smartcontractkit/chainlink-common v0.11.2-0.20260607151512-4747f7356241 h1:CxI0uK1QQZt2hXvtmTDkGxHZE6J+SQ13YFu6uGr0wJE= -github.com/smartcontractkit/chainlink-common v0.11.2-0.20260607151512-4747f7356241/go.mod h1:fP9RqD25/gTx3XqRstN8o4lAI3jp42vwJBLRZwRoOOM= +github.com/smartcontractkit/chainlink-common v0.11.2-0.20260607185208-f07e2a300359 h1:lGlG8cP8MdV+Zjx36GF9eorT7235jro7eXGClV4m9P4= +github.com/smartcontractkit/chainlink-common v0.11.2-0.20260607185208-f07e2a300359/go.mod h1:fP9RqD25/gTx3XqRstN8o4lAI3jp42vwJBLRZwRoOOM= github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.20260601211238-9f526774fef0 h1:NExKM/D0HneOq/N5LGTbkV4VOa0UHCvfTNEb4GqYpto= github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.20260601211238-9f526774fef0/go.mod h1:HmUyH2oD9m+GRpKq7q3vuRnm1F2Uczf/Nd1v3ipMSK8= github.com/smartcontractkit/chainlink-common/pkg/monitoring v0.0.0-20251215152504-b1e41f508340 h1:PsjEI+5jZIz9AS4eOsLS5VpSWJINf38clXV3wryPyMk= From ccd274ba6b84d97c257b44c93864a9374be40d9e Mon Sep 17 00:00:00 2001 From: Vladimir Shchukin Date: Wed, 17 Jun 2026 13:11:52 -0400 Subject: [PATCH 12/16] onobard v2 monitoring wip --- chain_capabilities/solana/actions/actions.go | 10 +++++++--- chain_capabilities/solana/go.mod | 2 ++ chain_capabilities/solana/go.sum | 2 -- chain_capabilities/solana/main.go | 5 +++-- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/chain_capabilities/solana/actions/actions.go b/chain_capabilities/solana/actions/actions.go index a40cc8860..6a443dce1 100644 --- a/chain_capabilities/solana/actions/actions.go +++ b/chain_capabilities/solana/actions/actions.go @@ -42,6 +42,8 @@ type Solana struct { transmissionInfoProvider TransmissionInfoProvider lggr logger.SugaredLogger chainSelector uint64 + capabilityID string + actionMetrics capmon.ActionMetrics txComputeLimit limits.BoundLimiter[uint32] reportSizeLimit limits.BoundLimiter[commoncfg.Size] beholderProcessor beholder.ProtoProcessor @@ -52,7 +54,7 @@ type Solana struct { func NewSolana(ctx context.Context, cfg *config.Config, s types.SolanaService, messageBuilder *monitoring.MessageBuilder, beholderProcessor beholder.ProtoProcessor, lggr logger.Logger, limitsFactory limits.Factory, - transmissionScheduler ts.TransmissionScheduler, chainSelector uint64, + transmissionScheduler ts.TransmissionScheduler, chainSelector uint64, capabilityID string, handler chainconsensus.RequestHandler, ) (*Solana, error) { client := newForwarderClient(s, lggr, cfg.CREForwarderAddress, cfg.CREForwarderState, cfg.Transmitter) @@ -64,6 +66,8 @@ func NewSolana(ctx context.Context, cfg *config.Config, s types.SolanaService, m readsEnabled: cfg.ReadsEnabled, SolanaService: s, chainSelector: chainSelector, + capabilityID: capabilityID, + actionMetrics: capmon.NewActionMetrics(capabilityID), lggr: logger.Sugared(lggr), forwarderClient: client, transmissionInfoProvider: provider, @@ -419,8 +423,8 @@ func (s *Solana) GetProgramAccounts( func (s *Solana) MonitoringContext() capmon.MonitoringContext { return capmon.MonitoringContext{ - Logger: s.lggr, - Processor: s.beholderProcessor, + Logger: s.lggr, + Metrics: s.actionMetrics, ExecCtx: func(metadata capabilities.RequestMetadata, tsStart time.Time) *capmon.ExecutionContext { return s.messageBuilder.BuildV2ExecutionContext(monitoring.TelemetryContext{ TsStart: tsStart.UnixMilli(), diff --git a/chain_capabilities/solana/go.mod b/chain_capabilities/solana/go.mod index 29f4eaa6e..6af6018c6 100644 --- a/chain_capabilities/solana/go.mod +++ b/chain_capabilities/solana/go.mod @@ -197,3 +197,5 @@ require ( ) replace github.com/gogo/protobuf => github.com/gogo/protobuf v1.3.2 + +replace github.com/smartcontractkit/chainlink-common => ../../../chainlink-common diff --git a/chain_capabilities/solana/go.sum b/chain_capabilities/solana/go.sum index e5886d1bf..27d77bace 100644 --- a/chain_capabilities/solana/go.sum +++ b/chain_capabilities/solana/go.sum @@ -629,8 +629,6 @@ github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20260310183131-8 github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20260310183131-8d0f0e383288/go.mod h1:jPHlo/IN2YAArI001JJixmm6ZHQwgnAVJXY8VBFiFTc= github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20260310183131-8d0f0e383288 h1:eEjTgIQn4RW0ZPRepUDYTdgGwaRCMawMwgXkHItUc9U= github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20260310183131-8d0f0e383288/go.mod h1:67YbnoglYD61Pz/jTVCgav9wFq7S35OU8UyQSvPllRw= -github.com/smartcontractkit/chainlink-common v0.11.2-0.20260607185208-f07e2a300359 h1:lGlG8cP8MdV+Zjx36GF9eorT7235jro7eXGClV4m9P4= -github.com/smartcontractkit/chainlink-common v0.11.2-0.20260607185208-f07e2a300359/go.mod h1:fP9RqD25/gTx3XqRstN8o4lAI3jp42vwJBLRZwRoOOM= github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.20260601211238-9f526774fef0 h1:NExKM/D0HneOq/N5LGTbkV4VOa0UHCvfTNEb4GqYpto= github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.20260601211238-9f526774fef0/go.mod h1:HmUyH2oD9m+GRpKq7q3vuRnm1F2Uczf/Nd1v3ipMSK8= github.com/smartcontractkit/chainlink-common/pkg/monitoring v0.0.0-20251215152504-b1e41f508340 h1:PsjEI+5jZIz9AS4eOsLS5VpSWJINf38clXV3wryPyMk= diff --git a/chain_capabilities/solana/main.go b/chain_capabilities/solana/main.go index 810da37f6..70da6f606 100644 --- a/chain_capabilities/solana/main.go +++ b/chain_capabilities/solana/main.go @@ -35,6 +35,7 @@ import ( caperrors "github.com/smartcontractkit/chainlink-common/pkg/capabilities/errors" "github.com/smartcontractkit/chainlink-common/pkg/capabilities/v2/chain-capabilities/solana" solcapserver "github.com/smartcontractkit/chainlink-common/pkg/capabilities/v2/chain-capabilities/solana/server" + capmon "github.com/smartcontractkit/chainlink-common/pkg/capabilities/v2/monitoring" ) const ( @@ -67,7 +68,7 @@ var _ solcapserver.ClientCapability = &capabilityGRPCService{} func main() { loopserver.ServeNew(CapabilityName, func(s *loop.Server) loop.StandardCapabilities { return solcapserver.NewClientServer(&capabilityGRPCService{lggr: s.Logger.Named(CapabilityName), limitsFactory: s.LimitsFactory}) - }) + }, loop.WithOtelViews(append(consMetrics.MetricViews(), capmon.MetricViews()...))) } func (c *capabilityGRPCService) ChainSelector() uint64 { @@ -219,7 +220,7 @@ func (c *capabilityGRPCService) Initialise(ctx context.Context, dependencies cor c.lggr.Warn("Initialising solana oracle required for chain reads is disabled") } - c.Solana, err = actions.NewSolana(ctx, cfg, solService, messageBuilder, processor, c.lggr, c.limitsFactory, scheduler, c.chainSelector, c.consensusHandler) + c.Solana, err = actions.NewSolana(ctx, cfg, solService, messageBuilder, processor, c.lggr, c.limitsFactory, scheduler, c.chainSelector, c.id, c.consensusHandler) if err != nil { return err } From d58500861677340877a9497833eec6b8b48e3b3a Mon Sep 17 00:00:00 2001 From: Vladimir Shchukin Date: Wed, 17 Jun 2026 22:57:20 -0400 Subject: [PATCH 13/16] implement new monitoring context --- chain_capabilities/solana/actions/actions.go | 16 ++-------- chain_capabilities/solana/go.mod | 4 +-- chain_capabilities/solana/go.sum | 2 ++ chain_capabilities/solana/main.go | 2 +- .../solana/monitoring/messages.go | 30 ++++++------------- 5 files changed, 16 insertions(+), 38 deletions(-) diff --git a/chain_capabilities/solana/actions/actions.go b/chain_capabilities/solana/actions/actions.go index 6a443dce1..72f45f405 100644 --- a/chain_capabilities/solana/actions/actions.go +++ b/chain_capabilities/solana/actions/actions.go @@ -42,8 +42,6 @@ type Solana struct { transmissionInfoProvider TransmissionInfoProvider lggr logger.SugaredLogger chainSelector uint64 - capabilityID string - actionMetrics capmon.ActionMetrics txComputeLimit limits.BoundLimiter[uint32] reportSizeLimit limits.BoundLimiter[commoncfg.Size] beholderProcessor beholder.ProtoProcessor @@ -54,7 +52,7 @@ type Solana struct { func NewSolana(ctx context.Context, cfg *config.Config, s types.SolanaService, messageBuilder *monitoring.MessageBuilder, beholderProcessor beholder.ProtoProcessor, lggr logger.Logger, limitsFactory limits.Factory, - transmissionScheduler ts.TransmissionScheduler, chainSelector uint64, capabilityID string, + transmissionScheduler ts.TransmissionScheduler, chainSelector uint64, handler chainconsensus.RequestHandler, ) (*Solana, error) { client := newForwarderClient(s, lggr, cfg.CREForwarderAddress, cfg.CREForwarderState, cfg.Transmitter) @@ -66,8 +64,6 @@ func NewSolana(ctx context.Context, cfg *config.Config, s types.SolanaService, m readsEnabled: cfg.ReadsEnabled, SolanaService: s, chainSelector: chainSelector, - capabilityID: capabilityID, - actionMetrics: capmon.NewActionMetrics(capabilityID), lggr: logger.Sugared(lggr), forwarderClient: client, transmissionInfoProvider: provider, @@ -423,14 +419,8 @@ func (s *Solana) GetProgramAccounts( func (s *Solana) MonitoringContext() capmon.MonitoringContext { return capmon.MonitoringContext{ - Logger: s.lggr, - Metrics: s.actionMetrics, - ExecCtx: func(metadata capabilities.RequestMetadata, tsStart time.Time) *capmon.ExecutionContext { - return s.messageBuilder.BuildV2ExecutionContext(monitoring.TelemetryContext{ - TsStart: tsStart.UnixMilli(), - RequestMetadata: metadata, - }) - }, + Logger: s.lggr, + MetricsAttributes: s.messageBuilder.CapabilityMetricsAttributes, } } diff --git a/chain_capabilities/solana/go.mod b/chain_capabilities/solana/go.mod index 6af6018c6..3f76df124 100644 --- a/chain_capabilities/solana/go.mod +++ b/chain_capabilities/solana/go.mod @@ -9,7 +9,7 @@ require ( github.com/smartcontractkit/capabilities/chain_capabilities/common v0.0.0-20260601161303-0b4b1ac1c3fb github.com/smartcontractkit/capabilities/libs v0.0.0-20260604135015-e9711af26f89 github.com/smartcontractkit/chain-selectors v1.0.100 - github.com/smartcontractkit/chainlink-common v0.11.2-0.20260607185208-f07e2a300359 + github.com/smartcontractkit/chainlink-common v0.11.2-0.20260618025318-173ee3a17e48 github.com/smartcontractkit/chainlink-framework/multinode v0.0.0-20260521164805-26d78d5e1243 github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260604171908-6734db2d444f github.com/smartcontractkit/chainlink-solana v1.3.1-0.20260605150919-6d1f761cc654 @@ -197,5 +197,3 @@ require ( ) replace github.com/gogo/protobuf => github.com/gogo/protobuf v1.3.2 - -replace github.com/smartcontractkit/chainlink-common => ../../../chainlink-common diff --git a/chain_capabilities/solana/go.sum b/chain_capabilities/solana/go.sum index 27d77bace..21552e7c6 100644 --- a/chain_capabilities/solana/go.sum +++ b/chain_capabilities/solana/go.sum @@ -629,6 +629,8 @@ github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20260310183131-8 github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20260310183131-8d0f0e383288/go.mod h1:jPHlo/IN2YAArI001JJixmm6ZHQwgnAVJXY8VBFiFTc= github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20260310183131-8d0f0e383288 h1:eEjTgIQn4RW0ZPRepUDYTdgGwaRCMawMwgXkHItUc9U= github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20260310183131-8d0f0e383288/go.mod h1:67YbnoglYD61Pz/jTVCgav9wFq7S35OU8UyQSvPllRw= +github.com/smartcontractkit/chainlink-common v0.11.2-0.20260618025318-173ee3a17e48 h1:dC1HGxSm0hoZm7FSbgZbymIhtXAJy7+zMnLN+aMOu1Y= +github.com/smartcontractkit/chainlink-common v0.11.2-0.20260618025318-173ee3a17e48/go.mod h1:fP9RqD25/gTx3XqRstN8o4lAI3jp42vwJBLRZwRoOOM= github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.20260601211238-9f526774fef0 h1:NExKM/D0HneOq/N5LGTbkV4VOa0UHCvfTNEb4GqYpto= github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.20260601211238-9f526774fef0/go.mod h1:HmUyH2oD9m+GRpKq7q3vuRnm1F2Uczf/Nd1v3ipMSK8= github.com/smartcontractkit/chainlink-common/pkg/monitoring v0.0.0-20251215152504-b1e41f508340 h1:PsjEI+5jZIz9AS4eOsLS5VpSWJINf38clXV3wryPyMk= diff --git a/chain_capabilities/solana/main.go b/chain_capabilities/solana/main.go index 70da6f606..302e7f75c 100644 --- a/chain_capabilities/solana/main.go +++ b/chain_capabilities/solana/main.go @@ -220,7 +220,7 @@ func (c *capabilityGRPCService) Initialise(ctx context.Context, dependencies cor c.lggr.Warn("Initialising solana oracle required for chain reads is disabled") } - c.Solana, err = actions.NewSolana(ctx, cfg, solService, messageBuilder, processor, c.lggr, c.limitsFactory, scheduler, c.chainSelector, c.id, c.consensusHandler) + c.Solana, err = actions.NewSolana(ctx, cfg, solService, messageBuilder, processor, c.lggr, c.limitsFactory, scheduler, c.chainSelector, c.consensusHandler) if err != nil { return err } diff --git a/chain_capabilities/solana/monitoring/messages.go b/chain_capabilities/solana/monitoring/messages.go index 3fe10d86a..772754155 100644 --- a/chain_capabilities/solana/monitoring/messages.go +++ b/chain_capabilities/solana/monitoring/messages.go @@ -37,27 +37,15 @@ func NewMessageBuilder(chainInfo types.ChainInfo, capInfo capabilities.Capabilit } } -// BuildV2ExecutionContext builds the v2 monitoring ExecutionContext used by the -// generated server (--with-monitoring) action lifecycle events. -func (m *MessageBuilder) BuildV2ExecutionContext(tc TelemetryContext) *capmon.ExecutionContext { - ec := m.BuildExecutionContext(tc) - return &capmon.ExecutionContext{ - MetaSourceId: ec.MetaSourceId, - MetaChainFamilyName: ec.MetaChainFamilyName, - MetaChainId: ec.MetaChainId, - MetaNetworkName: ec.MetaNetworkName, - MetaNetworkNameFull: ec.MetaNetworkNameFull, - MetaWorkflowId: ec.MetaWorkflowId, - MetaWorkflowOwner: ec.MetaWorkflowOwner, - MetaWorkflowExecutionId: ec.MetaWorkflowExecutionId, - MetaWorkflowName: ec.MetaWorkflowName, - MetaWorkflowDonId: ec.MetaWorkflowDonId, - MetaWorkflowDonConfigVersion: ec.MetaWorkflowDonConfigVersion, - MetaReferenceId: ec.MetaReferenceId, - MetaCapabilityType: ec.MetaCapabilityType, - MetaCapabilityId: ec.MetaCapabilityId, - MetaCapabilityTimestampStart: ec.MetaCapabilityTimestampStart, - MetaCapabilityTimestampEmit: ec.MetaCapabilityTimestampEmit, +// CapabilityMetricsAttributes returns capability-scoped OTel labels for v2 action metrics. +func (m *MessageBuilder) CapabilityMetricsAttributes() []attribute.KeyValue { + return []attribute.KeyValue{ + attribute.String(capmon.LabelChainFamilyName, capmon.ValOrUnknown(m.ChainInfo.FamilyName)), + attribute.String(capmon.LabelChainID, capmon.ValOrUnknown(m.ChainInfo.ChainID)), + attribute.String(capmon.LabelNetworkName, capmon.ValOrUnknown(m.ChainInfo.NetworkName)), + attribute.String(capmon.LabelNetworkNameFull, capmon.ValOrUnknown(m.ChainInfo.NetworkNameFull)), + attribute.String(capmon.LabelCapabilityType, capmon.ValOrUnknown(string(m.CapInfo.CapabilityType))), + attribute.String(capmon.LabelCapabilityID, capmon.ValOrUnknown(m.CapInfo.ID)), } } From 182babfb07379da5506d0eb7687d32cb6ffea211 Mon Sep 17 00:00:00 2001 From: Vladimir Shchukin Date: Wed, 17 Jun 2026 23:23:01 -0400 Subject: [PATCH 14/16] populate local chain info --- chain_capabilities/solana/main.go | 34 ++++++++++++++++-- chain_capabilities/solana/main_test.go | 49 ++++++++++++++++++++++++++ 2 files changed, 81 insertions(+), 2 deletions(-) create mode 100644 chain_capabilities/solana/main_test.go diff --git a/chain_capabilities/solana/main.go b/chain_capabilities/solana/main.go index 302e7f75c..e4c1a48e2 100644 --- a/chain_capabilities/solana/main.go +++ b/chain_capabilities/solana/main.go @@ -152,10 +152,17 @@ func (c *capabilityGRPCService) Initialise(ctx context.Context, dependencies cor } c.id = "solana" + ":ChainSelector:" + strconv.FormatUint(c.chainSelector, 10) + "@1.0.0" + c.CapabilityInfo = capabilities.CapabilityInfo{ + ID: c.id, + CapabilityType: capabilities.CapabilityTypeCombined, + Description: c.Description(), + IsLocal: cfg.IsLocal, + } var chainInfo types.ChainInfo - // protection for e2e tests when we run against local validator - if !cfg.IsLocal { + if cfg.IsLocal { + chainInfo = localChainInfo(cfg, c.chainSelector) + } else { chainInfo, err = relayer.GetChainInfo(ctx) if err != nil { return fmt.Errorf("failed to fetch chain info for chainID %s from relayer: %w", cfg.ChainID, err) @@ -266,6 +273,29 @@ func (s *capabilityGRPCService) setSelector(cfg *config.Config) error { return nil } +// localChainInfo builds monitoring labels for local CRE runs where the relayer +// cannot resolve chain metadata from a fixed genesis hash. +func localChainInfo(cfg *config.Config, chainSelector uint64) types.ChainInfo { + chainID := cfg.ChainID + if chainID == "" { + chainID = strconv.FormatUint(chainSelector, 10) + } + + networkName := cfg.Network + if networkName == "" { + networkName = "local" + } + + networkNameFull := networkName + "-local" + + return types.ChainInfo{ + FamilyName: "solana", + ChainID: chainID, + NetworkName: networkName, + NetworkNameFull: networkNameFull, + } +} + func (c *capabilityGRPCService) unmarshalConfig(configStr string) (*config.Config, error) { var cfg config.Config if err := json.Unmarshal([]byte(configStr), &cfg); err != nil { diff --git a/chain_capabilities/solana/main_test.go b/chain_capabilities/solana/main_test.go new file mode 100644 index 000000000..ee2f35636 --- /dev/null +++ b/chain_capabilities/solana/main_test.go @@ -0,0 +1,49 @@ +package main + +import ( + "testing" + + chain_selectors "github.com/smartcontractkit/chain-selectors" + + "github.com/smartcontractkit/capabilities/chain_capabilities/solana/config" +) + +func TestLocalChainInfo(t *testing.T) { + t.Parallel() + + selector := chain_selectors.TEST_22222222222222222222222222222222222222222222.Selector + info := localChainInfo(&config.Config{ + ChainID: "local-genesis-hash", + Network: "devnet", + }, selector) + + if info.FamilyName != "solana" { + t.Fatalf("FamilyName = %q, want solana", info.FamilyName) + } + if info.ChainID != "local-genesis-hash" { + t.Fatalf("ChainID = %q, want local-genesis-hash", info.ChainID) + } + if info.NetworkName != "devnet" { + t.Fatalf("NetworkName = %q, want devnet", info.NetworkName) + } + if info.NetworkNameFull != "devnet-local" { + t.Fatalf("NetworkNameFull = %q, want devnet-local", info.NetworkNameFull) + } +} + +func TestLocalChainInfoDefaults(t *testing.T) { + t.Parallel() + + selector := uint64(12345) + info := localChainInfo(&config.Config{}, selector) + + if info.ChainID != "12345" { + t.Fatalf("ChainID = %q, want 12345", info.ChainID) + } + if info.NetworkName != "local" { + t.Fatalf("NetworkName = %q, want local", info.NetworkName) + } + if info.NetworkNameFull != "local-local" { + t.Fatalf("NetworkNameFull = %q, want local-local", info.NetworkNameFull) + } +} From 27eb34c963a2f495f8b52a70c1a53c2aafb625a2 Mon Sep 17 00:00:00 2001 From: Vladimir Shchukin Date: Wed, 17 Jun 2026 23:23:19 -0400 Subject: [PATCH 15/16] rm tests --- chain_capabilities/solana/main_test.go | 49 -------------------------- 1 file changed, 49 deletions(-) delete mode 100644 chain_capabilities/solana/main_test.go diff --git a/chain_capabilities/solana/main_test.go b/chain_capabilities/solana/main_test.go deleted file mode 100644 index ee2f35636..000000000 --- a/chain_capabilities/solana/main_test.go +++ /dev/null @@ -1,49 +0,0 @@ -package main - -import ( - "testing" - - chain_selectors "github.com/smartcontractkit/chain-selectors" - - "github.com/smartcontractkit/capabilities/chain_capabilities/solana/config" -) - -func TestLocalChainInfo(t *testing.T) { - t.Parallel() - - selector := chain_selectors.TEST_22222222222222222222222222222222222222222222.Selector - info := localChainInfo(&config.Config{ - ChainID: "local-genesis-hash", - Network: "devnet", - }, selector) - - if info.FamilyName != "solana" { - t.Fatalf("FamilyName = %q, want solana", info.FamilyName) - } - if info.ChainID != "local-genesis-hash" { - t.Fatalf("ChainID = %q, want local-genesis-hash", info.ChainID) - } - if info.NetworkName != "devnet" { - t.Fatalf("NetworkName = %q, want devnet", info.NetworkName) - } - if info.NetworkNameFull != "devnet-local" { - t.Fatalf("NetworkNameFull = %q, want devnet-local", info.NetworkNameFull) - } -} - -func TestLocalChainInfoDefaults(t *testing.T) { - t.Parallel() - - selector := uint64(12345) - info := localChainInfo(&config.Config{}, selector) - - if info.ChainID != "12345" { - t.Fatalf("ChainID = %q, want 12345", info.ChainID) - } - if info.NetworkName != "local" { - t.Fatalf("NetworkName = %q, want local", info.NetworkName) - } - if info.NetworkNameFull != "local-local" { - t.Fatalf("NetworkNameFull = %q, want local-local", info.NetworkNameFull) - } -} From 433ee185c1294a8c29a224d89a6519ddaadd60a9 Mon Sep 17 00:00:00 2001 From: Vladimir Shchukin Date: Thu, 18 Jun 2026 01:41:25 -0400 Subject: [PATCH 16/16] bump common --- chain_capabilities/solana/go.mod | 2 +- chain_capabilities/solana/go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/chain_capabilities/solana/go.mod b/chain_capabilities/solana/go.mod index 3f76df124..dd91e904c 100644 --- a/chain_capabilities/solana/go.mod +++ b/chain_capabilities/solana/go.mod @@ -9,7 +9,7 @@ require ( github.com/smartcontractkit/capabilities/chain_capabilities/common v0.0.0-20260601161303-0b4b1ac1c3fb github.com/smartcontractkit/capabilities/libs v0.0.0-20260604135015-e9711af26f89 github.com/smartcontractkit/chain-selectors v1.0.100 - github.com/smartcontractkit/chainlink-common v0.11.2-0.20260618025318-173ee3a17e48 + github.com/smartcontractkit/chainlink-common v0.11.2-0.20260618045055-d385247612ec github.com/smartcontractkit/chainlink-framework/multinode v0.0.0-20260521164805-26d78d5e1243 github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260604171908-6734db2d444f github.com/smartcontractkit/chainlink-solana v1.3.1-0.20260605150919-6d1f761cc654 diff --git a/chain_capabilities/solana/go.sum b/chain_capabilities/solana/go.sum index 21552e7c6..56aedf7b7 100644 --- a/chain_capabilities/solana/go.sum +++ b/chain_capabilities/solana/go.sum @@ -629,8 +629,8 @@ github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20260310183131-8 github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20260310183131-8d0f0e383288/go.mod h1:jPHlo/IN2YAArI001JJixmm6ZHQwgnAVJXY8VBFiFTc= github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20260310183131-8d0f0e383288 h1:eEjTgIQn4RW0ZPRepUDYTdgGwaRCMawMwgXkHItUc9U= github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20260310183131-8d0f0e383288/go.mod h1:67YbnoglYD61Pz/jTVCgav9wFq7S35OU8UyQSvPllRw= -github.com/smartcontractkit/chainlink-common v0.11.2-0.20260618025318-173ee3a17e48 h1:dC1HGxSm0hoZm7FSbgZbymIhtXAJy7+zMnLN+aMOu1Y= -github.com/smartcontractkit/chainlink-common v0.11.2-0.20260618025318-173ee3a17e48/go.mod h1:fP9RqD25/gTx3XqRstN8o4lAI3jp42vwJBLRZwRoOOM= +github.com/smartcontractkit/chainlink-common v0.11.2-0.20260618045055-d385247612ec h1:YAFL8c7TN5choX7jf66wrr5FLr0n8hKJ/OyafNGSnrc= +github.com/smartcontractkit/chainlink-common v0.11.2-0.20260618045055-d385247612ec/go.mod h1:fP9RqD25/gTx3XqRstN8o4lAI3jp42vwJBLRZwRoOOM= github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.20260601211238-9f526774fef0 h1:NExKM/D0HneOq/N5LGTbkV4VOa0UHCvfTNEb4GqYpto= github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.20260601211238-9f526774fef0/go.mod h1:HmUyH2oD9m+GRpKq7q3vuRnm1F2Uczf/Nd1v3ipMSK8= github.com/smartcontractkit/chainlink-common/pkg/monitoring v0.0.0-20251215152504-b1e41f508340 h1:PsjEI+5jZIz9AS4eOsLS5VpSWJINf38clXV3wryPyMk=