From 1a4d95cb9fa137a160ccb19f2c9e1f1dca8757ab Mon Sep 17 00:00:00 2001 From: Hossein Boustani Date: Mon, 19 Oct 2026 23:29:52 +0330 Subject: [PATCH 1/2] fix(solana): accept version 1 transactions in getBlock and getTransaction Solana clusters (mainnet, testnet, devnet) now include version 1 transactions in most blocks. With maxSupportedTransactionVersion 0 the RPC rejects the whole block: "Transaction version (1) is not supported by the requesting client", so no Solana block can be indexed. Version 1 parses like version 0 for the transfers the indexer extracts (it adds transactionConfig and has no addressTableLookups), so raising the limit is enough. Co-Authored-By: Claude Opus 5.5 --- internal/rpc/solana/client.go | 4 ++-- internal/rpc/solana/types.go | 8 +++++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/internal/rpc/solana/client.go b/internal/rpc/solana/client.go index e919d2c..c5ffde6 100644 --- a/internal/rpc/solana/client.go +++ b/internal/rpc/solana/client.go @@ -53,7 +53,7 @@ func (c *Client) GetSlot(ctx context.Context) (uint64, error) { func (c *Client) GetTransaction(ctx context.Context, signature string) (*GetTransactionResult, error) { cfg := map[string]any{ "encoding": "jsonParsed", - "maxSupportedTransactionVersion": 0, + "maxSupportedTransactionVersion": MaxSupportedTransactionVersion, "commitment": "finalized", } resp, err := c.base.CallRPC(ctx, "getTransaction", []any{signature, cfg}) @@ -78,7 +78,7 @@ func (c *Client) GetBlock(ctx context.Context, slot uint64) (*GetBlockResult, er Encoding: "jsonParsed", TransactionDetails: "full", Rewards: false, - MaxSupportedTransactionVersion: 0, + MaxSupportedTransactionVersion: MaxSupportedTransactionVersion, Commitment: "finalized", } resp, err := c.base.CallRPC(ctx, "getBlock", []any{slot, cfg}) diff --git a/internal/rpc/solana/types.go b/internal/rpc/solana/types.go index 8db5c51..979067e 100644 --- a/internal/rpc/solana/types.go +++ b/internal/rpc/solana/types.go @@ -23,11 +23,17 @@ type jsonRPCError struct { type GetSlotResult uint64 +// MaxSupportedTransactionVersion is the newest transaction format getBlock and getTransaction may return. +// Clusters now include version 1 transactions in most blocks; asking for at most 0 makes the RPC reject the +// whole block ("Transaction version (1) is not supported by the requesting client"). Version 1 parses like +// version 0 for transfers (it adds a transactionConfig field and drops addressTableLookups). +const MaxSupportedTransactionVersion = 1 + type GetBlockConfig struct { Encoding string `json:"encoding"` // json | jsonParsed TransactionDetails string `json:"transactionDetails"` // full Rewards bool `json:"rewards"` // false - MaxSupportedTransactionVersion int `json:"maxSupportedTransactionVersion"` // 0 + MaxSupportedTransactionVersion int `json:"maxSupportedTransactionVersion"` // MaxSupportedTransactionVersion Commitment string `json:"commitment,omitempty"` // processed | confirmed | finalized } From a783a7c326b20f458b35af89dc73e5f5b151d576 Mon Sep 17 00:00:00 2001 From: Hossein Boustani Date: Wed, 21 Oct 2026 13:42:31 +0330 Subject: [PATCH 2/2] fix(bloomfilter): read BF.MEXISTS replies in RESP3 as well as RESP2 ContainsBatch only accepted int64 items. go-redis v9 speaks RESP3 by default, where RedisBloom answers BF.MEXISTS with booleans, so every address read as unmonitored and the EVM indexer matched no ERC-20 transfer (and no native transfer to a watched address) since the batch check replaced per-address lookups (#115). Accept both reply shapes. Co-Authored-By: Claude Opus 5.5 --- pkg/addressbloomfilter/redis.go | 17 ++++++++++++++--- pkg/addressbloomfilter/redis_reply_test.go | 22 ++++++++++++++++++++++ 2 files changed, 36 insertions(+), 3 deletions(-) create mode 100644 pkg/addressbloomfilter/redis_reply_test.go diff --git a/pkg/addressbloomfilter/redis.go b/pkg/addressbloomfilter/redis.go index f5a1351..57ff821 100644 --- a/pkg/addressbloomfilter/redis.go +++ b/pkg/addressbloomfilter/redis.go @@ -214,13 +214,24 @@ func (rbf *redisBloomFilter) ContainsBatch(addresses []string, addressType enum. return results } for i, item := range items { - if n, ok := item.(int64); ok { - results[i] = n == 1 - } + results[i] = bloomReplyTrue(item) } return results } +// bloomReplyTrue reads one BF.MEXISTS reply item. RESP2 answers 1 or 0; RESP3, go-redis v9's default protocol, +// answers true or false. Reading only integers would report every address as unwatched. +func bloomReplyTrue(item any) bool { + switch v := item.(type) { + case int64: + return v == 1 + case bool: + return v + default: + return false + } +} + func (rbf *redisBloomFilter) Clear(addressType enum.NetworkType) { rbf.mu.Lock() defer rbf.mu.Unlock() diff --git a/pkg/addressbloomfilter/redis_reply_test.go b/pkg/addressbloomfilter/redis_reply_test.go new file mode 100644 index 0000000..630482e --- /dev/null +++ b/pkg/addressbloomfilter/redis_reply_test.go @@ -0,0 +1,22 @@ +package addressbloomfilter + +import "testing" + +func TestBloomReplyTrue_AcceptsResp2AndResp3Replies(t *testing.T) { + cases := []struct { + item any + want bool + }{ + {int64(1), true}, // RESP2 + {int64(0), false}, // RESP2 + {true, true}, // RESP3 + {false, false}, // RESP3 + {"1", false}, + {nil, false}, + } + for _, c := range cases { + if got := bloomReplyTrue(c.item); got != c.want { + t.Errorf("bloomReplyTrue(%#v) = %v, want %v", c.item, got, c.want) + } + } +}