This suite tests a JSON-RPC server against MIP-16: JSON-RPC Query Methods. It covers these five methods:
eth_queryBlockseth_queryTransactionseth_queryLogseth_queryTraceseth_queryTransfers
All fixture data comes from Monad mainnet (chain ID 143, 0x8f).
MIP-16 lets each server make some decisions. Two correct servers can give different responses to the same request. The runner does not report these differences as failures, except for the availability window.
- Block tags. Each server resolves
latest,safe,finalized, andearliestat query time. The result is different between servers and between requests. Fixtures that use tags, or that omitfromBlockortoBlock, do not compare block numbers or hashes. They check only the format of the response and the general rules. - Availability window. Each node can serve a different range of blocks for each method. For example, a node can keep all blocks but only recent traces. If the server returns
-32001(resource not found) or-32004(method not supported), the fixture fails. To pass all fixtures, the node must serve all the blocks in Required Block Range for all five methods. - Error messages. MIP-16 specifies only the error
code. The runner compares onlyerror.code. It does not compareerror.messageorerror.data. - Budget. Each server sets its own budget, such as a maximum execution time or response size. When the server reaches its budget, it ends the page at an earlier block. Thus the number of pages for a query is different between servers. The runner requests all pages until the query is complete, and compares the merged result. See Pages and Budget. The runner cannot cause a server to reach its budget, so this suite does not test the
-32005(limit exceeded) error.
Install Bun. Then run the suite against your server:
bun install
RPC_URL=http://127.0.0.1:8080 bun testTo run the fixtures for one method only, use a name filter:
RPC_URL=http://127.0.0.1:8080 bun test -t eth_queryLogsThe runner sends the fixtures one at a time, in file order.
Each fetch request has a timeout of 30 seconds. When the timeout occurs, the runner stops the request, and the fixture fails. To change the timeout, set REQUEST_TIMEOUT_MS:
RPC_URL=http://127.0.0.1:8080 REQUEST_TIMEOUT_MS=60000 bun testFor each fetch request, the runner prints the duration, the fromBlock and toBlock that it sent, and the result. A fixture that paginates prints one line for each page:
035.json request 1 0x2faf088..0x2faf08c: 412 ms (HTTP 200)
035.json request 2 0x2faf089..0x2faf08a: 230 ms (HTTP 200)
| Field | Type | Description |
|---|---|---|
description |
string |
What the fixture tests. Refer to the MIP-16 section. |
request |
object |
The full JSON-RPC request. The runner sends it without change. |
response |
object |
The expected JSON-RPC response. |
Example:
{
"description": "Filter USDC Transfer logs by address and topic, and join blocks. See MIP-16 Example.",
"request": {
"jsonrpc": "2.0",
"id": 1,
"method": "eth_queryLogs",
"params": [{
"filter": {
"address": "0x754704bc059f8c67012fed69bc8a327a5aafb603",
"topics": ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"]
},
"fields": {
"logs": ["blockNumber", "logIndex", "address", "data", "topics"],
"blocks": ["number", "timestamp"]
},
"order": "asc",
"fromBlock": "0x5e69ec4",
"toBlock": "0x5e69ecb"
}]
},
"response": {
"jsonrpc": "2.0",
"id": 1,
"result": { "...": "..." }
}
}The runner does these checks in order, and stops at the first stage that fails. The checks are in utils.ts.
| Stage | Checks |
|---|---|
| Transport | The response arrives before REQUEST_TIMEOUT_MS and is JSON. |
| Envelope | jsonrpc is "2.0", id matches, and the response has result or error, but not both. |
| Error | An expected error has the same error.code. An unexpected error fails. |
| Page | On each page: valid block references, the correct keys and encodings, sort order without duplicates, relations without duplicates or missing references, objects only in the page's blocks, and no page that ends after the block where target ends it. |
| Pagination | Each next page succeeds, starts at the requested fromBlock, has the same toBlock, and links to the previous page by hash. |
| Comparison | The merged result is equal to the expected result. Key order and hex letter case are not significant. |
Some page checks need specific fields, such as the fields of the ordering key. If a fixture does not select them, only the comparison finds the problem.
Each fixture contains the result of the complete range. The runner requests pages until cursorBlock is toBlock, changes only fromBlock for each next page, and compares the merged result.
A page can end early because of target or because of the server budget. The runner cannot tell which. A page must not end after the block where target ends it, but it can end before that block. Then the runner prints a note.
Fixtures 020–029 and 035–039 paginate because of target. Any fixture can paginate because of the budget. A server that ends the page early gets a note, not a failure, because the runner cannot tell this defect from the budget.
The fixtures use Monad mainnet blocks 50,000,005 to 50,000,018 (0x2faf085 to 0x2faf092). The node must serve this range for all five methods.
MIP-16 does not yet specify these items. The fixtures do not test them until MIP-16 specifies them.
- Hex letter case. MIP-16 does not specify lowercase or uppercase hex in responses. The MIP-16 example uses both forms and also an EIP-55 checksum address.
- Unselected fields. MIP-16 does not state that a server must not return fields that the request does not select.
- Empty filter arrays. MIP-16 does not specify the result of a filter such as
"from": []. - Unknown field names. MIP-16 specifies
-32602for an unknownfieldskey, but not for an unknown field name in afieldsarray. - Signature encoding. MIP-16 specifies
randsasDATA. Nodes return them asQUANTITY, as ineth_getTransactionByHash, so a value can have an odd number of hex digits. The fixtures and the runner useQUANTITY. - Trace error text. MIP-16 does not specify the
errorstring of a trace. The fixtures contain the text from the MonadcallTracer, for example"error". A different correct server can use different text.