[CON-905] Supersede #8: correct the Streams and Backfills references, verified live - #14
Open
0xsergen wants to merge 6 commits into
Open
[CON-905] Supersede #8: correct the Streams and Backfills references, verified live#140xsergen wants to merge 6 commits into
0xsergen wants to merge 6 commits into
Conversation
Every JavaScript filter example in this reference threw or silently returned
nothing, and the documented `dataset` values are rejected by the API. Agents read
this file to generate filter code, so each defect became broken generated code.
Verified with POST /streams/test_filter (read-only, creates nothing) on Ethereum
blocks 21000000 / 21000280 and Solana slot 300000000.
## Defects
1. dataset enum — `log`, `transaction`, `receipt` are rejected with HTTP 400; the
API accepts `logs`, `transactions`, `receipts`. The table listed 5 of 19 valid
values; now complete, sourced from the OpenAPI enum.
2. stream.data shape — it is an array whose nesting depends on the dataset, and
was treated as a single object throughout. All 12 runnable examples failed:
Block / Receipt Streams Cannot read properties of undefined ('length')
Transaction / Utilities Cannot convert undefined to a BigInt
Logs / NFT Transfer Cannot read properties of undefined ('0')
DEX / Monitor Contract Cannot read properties of undefined ('toLowerCase')
Function Signature processedData is not defined
Whale, Solana x2 silent null — no error, no data
For `logs` the middle dimension is TRANSACTIONS, not blocks: the real shape is
data[block][tx][log]. On block 21000000 a block's entry holds 181 groups, one
per transaction, each group's logs sharing one transactionHash.
3. Batch safety — the outer dimension is the batch. `stream.data.length` equals
`dataset_batch_size` (default 1), so indexing `stream.data[0]` silently
processes only the first block. Every example now iterates. This is not
hypothetical: 9 of 20 streams on our own account run batch sizes of 5 or 50.
4. auth header — Management API examples used `Authorization: Bearer`; the API's
only securityScheme is `x-api-key`. The webhook destination's own Bearer
header is unrelated and left alone.
5. Create Stream body — was camelCase `filterFunction` with a nested
`destination` object. The API takes snake_case `filter_function`
(base64-encoded), a `destination` string enum, and separate
`destination_attributes`. Added the required-field list.
6. metadata field names — documented as `streamId`; the API returns `stream_id`,
and every metadata key is snake_case.
7. DEX swap example — filtered `Swap` events by router address, but the emitter
is always the pool. Block 21000000 has 22 Swap events and zero from a router,
so it could never match at any nesting depth. Now filters by event signature.
8. Solana examples — `programs_with_logs` items have no `programId`; it lives at
`programInvocations[].programId`. Balance deltas come from
`instruction.accounts[].pre/postBalance`, not `meta.pre/postBalances`.
## Additions
- A `**Test:**` line on every example naming the network, block, and the result
it produces, so a reader can confirm their setup before adapting the code.
Track Whale Transactions is pinned to 21000280 (a 2143 ETH transfer) because
1000+ ETH transfers are absent from most blocks.
- Payload Shape section documenting nesting per dataset and the batch dimension.
- Test a Filter section covering POST /streams/test_filter, including that
`block` must be a string and that a thrown filter still returns HTTP 201 with
the error nested in `result.error`.
## Verified after the change
Block Streams block @ 21000000 ok
Transaction Streams transactions @ 21000000 ok
Logs Streams logs @ 21000000 ok
Receipt Streams receipts @ 21000000 ok
Function Signature logs @ 21000000 ok
Available Utilities transactions @ 21000000 ok
Complex Filter Example logs @ 21000000 ok
Monitor Specific Contract logs @ 21000000 ok
Track Whale Transactions transactions @ 21000280 ok
NFT Transfer Tracking logs @ 21000000 ok
Monitor Program Logs programs_with_logs @ 300000000 ok
Track SOL Transfers programs_with_logs @ 300000000 ok
Key-Value Store Integration is the only example not executed; it needs a
pre-populated KV list.
(cherry picked from commit a6e514b)
Audited streams-backfills-reference.md against qn 0.6.0, @quicknode/sdk 3.8.2, and raw REST. 9 confirmed defects. `test_filter` is free and creates nothing, so it was the validation lane. No Stream was created. Wrong values: - The EVM trace dataset slugs did not exist. `debug_trace` and `trace_block` are rejected at every layer. The CLI and SDK take `debug-traces` and `trace-blocks`. - The destination list gave display names and was incomplete. The five wire values are `webhook`, `s3`, `azure`, `postgres`, `kafka`. Kafka was missing. "PostgreSQL" and "Azure Storage" are rejected. - Compression was listed as a Stream setting. It is a destination attribute, and Kafka names it `compressionType`. Omissions that made the guidance wrong: - `qn stream create` builds webhook destinations only. For S3, Azure, Postgres, or Kafka you must use `--stream-config-file`, which ignores every other flag. An agent asked for an S3 backfill could not express it, and no missing flag signalled the problem. - `decodeEVMReceipts` was named with no signature. It is `decodeEVMReceipts(receipts, abis)`, where `abis` is an array of ABI arrays. It adds `decodedLogs` to each receipt. It does not touch `logs` and adds no `decoded` key. An agent reading `logs[].decoded` gets undefined for every log and concludes nothing matched. Measured on block 17811625: 135 receipts in, 135 out, 46 with decodedLogs, 110 decoded logs. - Bitcoin Cash and XRPL were named without their network keys. They are `bch-mainnet` and `xrp-mainnet`. Note `xrplevm-mainnet` is a different chain. - The API Credits Calculator had no link. It is embedded in the Streams billing docs; there is no standalone page. - Continuing a backfill into live delivery needs an end range of -1. - Elastic Batch and batch size had no field names. They are `elasticBatchEnabled` and `datasetBatchSize`, and both are required in CreateStreamParams alongside `startRange` and `endRange`. Also confirmed: there is no /backfills REST resource, filtering does not reduce credits, and all 7 documentation links return 200. `qn stream create` was never executed. Creating a backfill bills credits, so that path stays help-and-parse verified.
The cherry-picked commit executed 12 of its 13 examples. Its own
message names the Key-Value Store example as the one it could not run,
because that needs a pre-populated KV list. CON-905 seeded the list and
ran it. Both remaining defects are silent failures.
1. `new Set(await qnLib.qnContainsListItems('watchlist', senders))`
then `.has(tx.from)`. The helper returns booleans aligned to the
input by position, not the matched values, so the Set holds `true`
and `false` and `.has(address)` is always false. The branch is dead
and the filter returns null on every block.
Fixed with `senders.filter((_, i) => flags[i])`.
2. `qnAddSet('watched_txs', tx.hash, JSON.stringify({...}))`.
`qnAddSet.length` is 2. The third argument is dropped without an
error, so the JSON payload is discarded and `tx.hash` is stored as
the value. Every hit also overwrote the same single key.
Fixed to `qnAddSet(`watched:${tx.hash}`, JSON.stringify({...}))`,
one key per transaction.
Kept from the cherry-picked commit: `stream.data.flatMap(block => block)`.
Indexing `stream.data[0]` processes only the first block of a batch, and
batch sizes above 1 are in real use. An earlier CON-905 draft
reintroduced that index; this version does not.
Added a table for the three qnLib signatures that fail silently, and a
note that every write helper returns "OK" even on a no-op.
The cherry-picked commit put a Test: line on 12 of 13 examples. The Key-Value Store example was the exception, because it needs a pre-populated list. Seeded one and ran it, so it now carries the 13th. Test: ethereum-mainnet block 21000000 gives 181 transactions and 169 unique senders. With two of those senders on the watchlist the filter returns 2 watchlist_hit objects. The same run against the previous code returned 0. Replaced the trap-shaped table with a plain signature table, per the house rule that a reference states facts rather than warnings. Added qnGetList and qnGetSet, both checked live: a missing list key gives [], a missing set key gives null. Dropped a claim that qnContainsListItems caps at 1,500 items per call. It does not. A 1,501-item call returned 1,501 booleans. The 1,500 cap is write-only, on addItems plus removeItems, and kv-reference.md already scopes it that way.
CON-901 asks for a Test: line on every example. The dataset slugs and the network keys were the two defect clusters in this file, so both enumerations now carry one. Verified live with `qn stream test-filter`, which is free and creates nothing: - All nine asserted EVM dataset slugs pass on ethereum-mainnet block 21000000: block, block-with-receipts, transactions, logs, receipts, debug-traces, trace-blocks, block-with-receipts-debug-trace, block-with-receipts-trace-block. The two previously documented spellings, debug_trace and trace_block, are rejected by the CLI's own enum. - bitcoin-mainnet and bch-mainnet pass on block 800000, and xrp-mainnet passes on ledger 80000000. bitcoin-cash-mainnet and xrpl-mainnet are rejected with the full list of valid keys. The `qn stream create` example carries no Test: line. Creating a backfill bills credits against a real historical range, so it stays help-and-parse verified only. Recorded as not-covered in the report.
The sync runbook in PRODUCT_CATALOG.md step 7 asks for a patch bump on renames, wording fixes, and stale-example cleanup. The CON-901 audit is all three, across CON-906, CON-902, and CON-905. One bump for all three PRs, in this one, because three PRs each editing the same manifests would conflict. The CON-906 and CON-902 PR bodies say the bump lands here. Runbook step 7 is itself wrong and is not fixed here. It names `plugins/build-web3/.claude-plugin/marketplace.json`, which does not exist, and it omits two files that do carry the version: `.codex-plugin/plugin.json` and `server.json`. Following the runbook literally leaves two of four manifests behind. All four are bumped here. The runbook text belongs to CON-917.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of the CON-901 reference audit. Covers CON-905:
streams-backfills-reference.mdandstreams-reference.md.This supersedes #8. Read the next section before the rest.
Relationship to #8
#8 has been open since 2026-08-18 with no reviews. Its work is real and most of it is correct, so it is carried here rather than discarded: commit
a6e514bis cherry-picked onto this branch with-x, so the original authorship stays in the history.Two bugs remained in #8's KV filter example. Both are silent.
new Set(await qnLib.qnContainsListItems('watchlist', senders))then.has(tx.from). That helper returns positional booleans, so the Set holdstrueandfalse, and.has(address)is never true.qnAddSet('watched_txs', tx.hash, JSON.stringify(...)).qnAddSettakes two arguments. The third is dropped without an error.The parallel CON-905 draft fixed both, but reintroduced
stream.data[0], which #8 itself correctly documents as dropping most of a batch. Neither version was right on its own.The reconciled form takes the correct part of each: #8's batch-safe
stream.data.flatMap(block => block), plus the positionalflags[i]lookup, plus the two-argumentqnAddSet(key, value).Proof, not reasoning
The reconciliation was verified live, not argued. A KV list was seeded with two real sender addresses from
ethereum-mainnetblock 21000000. Both forms then ran in a single filter throughqn stream test-filter:[true, false]qnAddSet.lengthThe two
watchlist_hitobjects came back, and the writes read back with the JSON payload intact.stream.data[0]is unsafe on this account today, not just in theory. Of 20 streams, 7 rundataset_batch_size > 1and 12 have elastic batching enabled. Elastic batching changes the effective batch at runtime, so a batch size of 1 in the config is not a guarantee either.A false claim caught before it shipped
A draft comment in the example read "The cap is 1,500 items per call." That is wrong. Verified live:
qnContainsListItemswith 1,501 items returns 1,501 booleans. The 1,500 cap is write-only, onaddItems+removeItemsinPATCH /lists. The claim was removed.kv-reference.mdalready scoped it correctly.qnLibreturn values, now stated as factqnContainsListItems(key, items)itemsby positionqnContainsListItem(key, item)qnGetList(key)[]for a key that does not existqnGetSet(key)nullfor a key that does not existqnAddSet(key, value)"OK". Arity is 2Every
qnLibwrite helper returns"OK". That value reports that the call was accepted, not that it changed anything.streams-backfills-reference.md12 defects fixed. The two clusters were the dataset slugs and the network keys. Both are now verified with
qn stream test-filter, which is free and creates nothing.ethereum-mainnetblock 21000000:block,block-with-receipts,transactions,logs,receipts,debug-traces,trace-blocks,block-with-receipts-debug-trace,block-with-receipts-trace-block. The two previously documented spellings,debug_traceandtrace_block, are rejected by the CLI's own enum.bitcoin-mainnetandbch-mainnetpass on block 800000.xrp-mainnetpasses on ledger 80000000.bitcoin-cash-mainnetandxrpl-mainnetare rejected, with the full list of valid keys in the error.Test: lines
15 across the two files.
streams-reference.md: 13 of 19 runnable blocks. All 13 filter examples carry one (#8 supplied 12, and the KV example is the 13th). Each pins a block and asserts a count a reader can reproduce: 187 Transfer logs atethereum-mainnet21000000, 22 swaps, 123 SPL Token transfers atsolana-mainnet300000000.The 6 without one are all Stream Management API calls. They create, update, pause, or delete a Stream. Every one mutates account state, so none can carry an asserted value.
streams-backfills-reference.md: 2, both on tables. Its one runnable block isqn stream create, which bills credits against a real historical range. It carries noTest:line, and it is verified for help text and argument parsing only. The 2 lines assert the dataset-slug and network-key enumerations instead, which is the contract working as intended: assert what you verified, and do not invent a value for a billed call.This file is where the
Test:format works best. A filter example is pure: pin a block, run it, and every reader gets the same number. Comparecli-reference.mdin PR #12, which reaches only 8 of 33, because most CLI examples are account-scoped.Version bump
The build-web3 patch bump to 1.0.3 is in this PR. Merge this one last.
The CON-906 and CON-902 PRs deliberately carry no bump, because three PRs each editing the same four manifests would conflict.
The sync runbook in
PRODUCT_CATALOG.mdstep 7 is wrong and is not fixed here. It namesplugins/build-web3/.claude-plugin/marketplace.json, which does not exist, and it omits two files that do carry the version:.codex-plugin/plugin.jsonandserver.json. Following it literally leaves two of four manifests behind. All four are bumped here. The runbook text is assigned to CON-917.Related: CON-906 and CON-902 PRs cover the other three files from the same audit pass.