diff --git a/rpc/ws/blockSubscribe.go b/rpc/ws/blockSubscribe.go index 6886a64d..0444e5e3 100644 --- a/rpc/ws/blockSubscribe.go +++ b/rpc/ws/blockSubscribe.go @@ -61,7 +61,13 @@ func NewBlockSubscribeFilterMentionsAccountOrProgram(pubkey solana.PublicKey) *B type BlockSubscribeOpts struct { Commitment rpc.CommitmentType - Encoding solana.EncodingType `json:"encoding,omitempty"` + // Encoding controls how transactions inside each block are returned. + // + // Supported values: EncodingBase58, EncodingBase64, EncodingBase64Zstd. + // EncodingJSONParsed is NOT supported here because BlockResult is shaped + // for the non-parsed transaction layout. Use Client.ParsedBlockSubscribe + // instead when parsed JSON output is needed. + Encoding solana.EncodingType `json:"encoding,omitempty"` // Level of transaction detail to return. TransactionDetails rpc.TransactionDetailsType @@ -100,11 +106,18 @@ func (cl *Client) BlockSubscribe( obj["commitment"] = opts.Commitment } if opts.Encoding != "" { + // EncodingJSONParsed cannot decode through BlockResult: the + // response shape uses *rpc.GetBlockResult, which models the + // non-parsed transaction layout. Reject it up front with a + // pointer to ParsedBlockSubscribe rather than letting the + // request go out and fail at decode time. + if opts.Encoding == solana.EncodingJSONParsed { + return nil, fmt.Errorf("encoding %s is not supported by BlockSubscribe; use ParsedBlockSubscribe instead", opts.Encoding) + } if !solana.IsAnyOfEncodingType( opts.Encoding, // Valid encodings: // solana.EncodingJSON, // TODO - solana.EncodingJSONParsed, // TODO solana.EncodingBase58, solana.EncodingBase64, solana.EncodingBase64Zstd, diff --git a/rpc/ws/blockSubscribe_test.go b/rpc/ws/blockSubscribe_test.go new file mode 100644 index 00000000..14e6c44f --- /dev/null +++ b/rpc/ws/blockSubscribe_test.go @@ -0,0 +1,53 @@ +// Copyright 2026 github.com/gagliardetto +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ws + +import ( + "strings" + "testing" + + "github.com/gagliardetto/solana-go" + "github.com/stretchr/testify/require" +) + +// TestBlockSubscribeRejectsJSONParsedEncoding verifies that BlockSubscribe +// fails fast when the caller asks for EncodingJSONParsed. The response struct +// (BlockResult) models the non-parsed transaction layout; letting the request +// go out anyway only surfaces as a confusing decode error on the first +// notification (issue #291). The rejection should also point the caller at +// ParsedBlockSubscribe so the fix is obvious. +func TestBlockSubscribeRejectsJSONParsedEncoding(t *testing.T) { + cl := &Client{} + _, err := cl.BlockSubscribe(NewBlockSubscribeFilterAll(), &BlockSubscribeOpts{ + Encoding: solana.EncodingJSONParsed, + }) + require.Error(t, err) + if !strings.Contains(err.Error(), "ParsedBlockSubscribe") { + t.Fatalf("error should mention ParsedBlockSubscribe, got: %v", err) + } +} + +// TestBlockSubscribeRejectsUnsupportedEncoding verifies the existing +// "unsupported encoding" rejection path is unchanged for arbitrary values. +func TestBlockSubscribeRejectsUnsupportedEncoding(t *testing.T) { + cl := &Client{} + _, err := cl.BlockSubscribe(NewBlockSubscribeFilterAll(), &BlockSubscribeOpts{ + Encoding: solana.EncodingType("not-a-real-encoding"), + }) + require.Error(t, err) + if !strings.Contains(err.Error(), "not supported") { + t.Fatalf("error should describe an unsupported encoding, got: %v", err) + } +}