Skip to main content

Streams Filter Examples (Go & JavaScript)

Updated on
Jul 20, 2026

This page provides ready-to-use filter examples for common use cases. Copy and modify these examples to match your specific needs.

Before You Start

Every filter function must follow these rules:


  • Name: Your function must be named Filter (Go) or main (JavaScript)
  • Return value: Return data to deliver it, or return nil/null to skip delivery for that block
  • Input: The function receives a payload with .Data (Go) / .data (JavaScript) and .Metadata / .metadata

type Payload struct {
Data []interface{} `json:"data"`
Metadata map[string]interface{} `json:"metadata"`
}

func Filter(qn *qn.QNContext, payload Payload) interface{} {
// payload.Data contains the blockchain data
// payload.Metadata contains stream information
return payload.Data // Return data to deliver, or nil to skip
}

Basic Examples

Extract Block Hash and Number

Dataset: block

Extracts the block hash and converts the block number from hex to decimal.


type Payload struct {
Data []Block `json:"data"`
Metadata map[string]interface{} `json:"metadata"`
}
type Block struct {
Hash string `json:"hash"`
Number string `json:"number"`
}

func Filter(qn *qn.QNContext, payload Payload) interface{} {
numberDecimal, _ := strconv.ParseInt(strings.TrimPrefix(payload.Data[0].Number, "0x"), 16, 64)

return map[string]interface{}{
"hash": payload.Data[0].Hash,
"number": numberDecimal,
}
}

Filter ERC-20 Transfer Events

Dataset: block_with_receipts

Finds all ERC-20 token transfers in a block. The Transfer event has a specific topic signature and exactly 3 topics (event signature + from + to addresses).


type Payload struct {
Data []Entry `json:"data"`
Metadata map[string]interface{} `json:"metadata"`
}
type Entry struct {
// Keep receipts as raw JSON so the full original object is preserved —
// only Receipt/Log below are decoded to evaluate the filter condition.
Receipts []json.RawMessage `json:"receipts"`
}
type Receipt struct {
Logs []Log `json:"logs"`
}
type Log struct {
Topics []string `json:"topics"`
}

func Filter(qn *qn.QNContext, payload Payload) interface{} {
const transferTopic = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"

var filteredReceipts []json.RawMessage
totalReceipts := 0
for _, entry := range payload.Data {
totalReceipts += len(entry.Receipts)
for _, raw := range entry.Receipts {
var receipt Receipt
if err := json.Unmarshal(raw, &receipt); err != nil {
continue
}
relevant := false
for _, log := range receipt.Logs {
if len(log.Topics) == 3 && log.Topics[0] == transferTopic {
relevant = true
break
}
}
if relevant {
filteredReceipts = append(filteredReceipts, raw)
}
}
}

if len(filteredReceipts) == 0 {
return nil
}

return map[string]interface{}{
"totalReceipts": totalReceipts,
"filteredCount": len(filteredReceipts),
"receipts": filteredReceipts,
}
}

Monitor Specific Addresses

Dataset: block_with_receipts

Tracks transactions and events involving a list of addresses. Useful for monitoring wallets or contracts.


type Payload struct {
Data []Entry `json:"data"`
Metadata map[string]interface{} `json:"metadata"`
}
type Entry struct {
Block Block `json:"block"`
Receipts []Receipt `json:"receipts"`
}
type Block struct {
Transactions []Transaction `json:"transactions"`
}
type Transaction struct {
From string `json:"from"`
To string `json:"to"`
}
type Receipt struct {
Logs []Log `json:"logs"`
}
type Log struct {
Topics []string `json:"topics"`
}

func Filter(qn *qn.QNContext, payload Payload) interface{} {
addresses := []string{
"0x56220b7e25c7d0885159915cdebf5819f2090f57",
"0x351e1b4079cf180971025a3b35dadea1d809de26",
"0xa61551e4e455edebaa7c59f006a1d2956d46eecc",
}

addressSet := make(map[string]bool)
paddedAddressSet := make(map[string]bool)
for _, addr := range addresses {
lower := strings.ToLower(addr)
addressSet[lower] = true
hex := lower[2:]
paddedAddressSet["0x"+strings.Repeat("0", 64-len(hex))+hex] = true
}

var matchingTransactions []Transaction
var matchingReceipts []Receipt

for _, entry := range payload.Data {
for _, tx := range entry.Block.Transactions {
from := strings.ToLower(tx.From)
to := strings.ToLower(tx.To)
if (tx.From != "" && addressSet[from]) || (tx.To != "" && addressSet[to]) {
matchingTransactions = append(matchingTransactions, tx)
}
}

for _, receipt := range entry.Receipts {
matches := false
for _, log := range receipt.Logs {
if len(log.Topics) > 1 && (paddedAddressSet[log.Topics[1]] ||
(len(log.Topics) > 2 && paddedAddressSet[log.Topics[2]])) {
matches = true
break
}
}
if matches {
matchingReceipts = append(matchingReceipts, receipt)
}
}
}

if len(matchingTransactions) == 0 && len(matchingReceipts) == 0 {
return nil
}

return map[string]interface{}{
"transactions": matchingTransactions,
"receipts": matchingReceipts,
}
}

Decoding EVM Events

The decodeEVMReceipts (JavaScript) / qn.DecodeEVMReceipts (Go) function transforms raw event logs into human-readable data using contract ABIs. This section covers the basics of decoding and then shows practical examples that combine decoding with filtering for common use cases like tracking token transfers, NFT sales, and DeFi activity.

How it works:

  1. Matches event signatures in logs with the provided ABIs
  2. Decodes parameters according to their types (addresses, integers, etc.)
  3. Returns structured data with named parameters in a decodedLogs array

Parameters:

  • receipts — Array of transaction receipts to decode
  • abis — Array of contract ABIs (strings or objects)

Store ABIs Externally

Instead of hardcoding ABIs in your filter, you can store them in Key-Value Store for easier updates and reuse. See Storing Contract ABIs.

Decode ERC-20 Transfers

Dataset: block_with_receipts

Decodes Transfer events into readable format with from, to, and value fields.


type Payload struct {
Data []Entry `json:"data"`
Metadata map[string]interface{} `json:"metadata"`
}
type Entry struct {
Receipts []interface{} `json:"receipts"`
}

func Filter(qn *qn.QNContext, payload Payload) interface{} {
const erc20ABI = `[{
"anonymous": false,
"inputs": [
{"indexed": true, "type": "address", "name": "from"},
{"indexed": true, "type": "address", "name": "to"},
{"indexed": false, "type": "uint256", "name": "value"}
],
"name": "Transfer",
"type": "event"
}]`

// Collect receipts from every block in the batch
var receipts []interface{}
for _, entry := range payload.Data {
receipts = append(receipts, entry.Receipts...)
}
decoded := qn.DecodeEVMReceipts(receipts, []string{erc20ABI})

// Filter for receipts with decoded logs
var result []interface{}
for _, r := range decoded {
receipt, ok := r.(map[string]interface{})
if !ok {
continue
}
if logs, ok := receipt["decodedLogs"].([]interface{}); ok && len(logs) > 0 {
result = append(result, receipt)
}
}

return map[string]interface{}{"result": result}
}

Decoded output example:

{
"address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
"name": "Transfer",
"from": "0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640",
"to": "0x6f1cdbbb4d53d226cf4b917bf768b94acbab6168",
"value": "138908045566"
}

Track NFT Marketplace Sales (OpenSea)

Dataset: block_with_receipts

Decodes OpenSea Seaport OrderFulfilled events to track NFT sales.


type Payload struct {
Data []Entry `json:"data"`
Metadata map[string]interface{} `json:"metadata"`
}
type Entry struct {
Receipts []interface{} `json:"receipts"`
}

func Filter(qn *qn.QNContext, payload Payload) interface{} {
// OpenSea Seaport OrderFulfilled event
// test on Ethereum, 21292520 block
const seaportABI = `[{
"anonymous": false,
"inputs": [
{"type": "bytes32", "name": "orderHash", "indexed": false},
{"type": "address", "name": "offerer", "indexed": true},
{"type": "address", "name": "zone", "indexed": true},
{"type": "address", "name": "recipient", "indexed": false},
{
"components": [
{"type": "uint8", "name": "itemType"},
{"type": "address", "name": "token"},
{"type": "uint256", "name": "identifier"},
{"type": "uint256", "name": "amount"}
],
"type": "tuple[]",
"name": "offer",
"indexed": false
},
{
"components": [
{"type": "uint8", "name": "itemType"},
{"type": "address", "name": "token"},
{"type": "uint256", "name": "identifier"},
{"type": "uint256", "name": "amount"},
{"type": "address", "name": "recipient"}
],
"type": "tuple[]",
"name": "consideration",
"indexed": false
}
],
"name": "OrderFulfilled",
"type": "event"
}]`

const seaportAddress = "0x00000000006c3852cbef3e08e8df289169ede581"

// Collect receipts from every block in the batch
var receipts []interface{}
for _, entry := range payload.Data {
receipts = append(receipts, entry.Receipts...)
}
decoded := qn.DecodeEVMReceipts(receipts, []string{seaportABI})

var result []interface{}
for _, r := range decoded {
receipt, ok := r.(map[string]interface{})
if !ok {
continue
}
logs, ok := receipt["decodedLogs"].([]interface{})
if !ok {
continue
}
for _, l := range logs {
logMap, ok := l.(map[string]interface{})
if !ok {
continue
}
address := strings.ToLower(fmt.Sprintf("%v", logMap["address"]))
name := fmt.Sprintf("%v", logMap["name"])
if address == seaportAddress && name == "OrderFulfilled" {
result = append(result, receipt)
break
}
}
}

return map[string]interface{}{"result": result}
}

Next Steps


Share this doc