Asentum

Reference

JSON-RPC Reference

Every eth_*, net_* and web3_* method a node answers · Estimated read time: 12 minutes

Accurate as of node 0.8.1.

TL;DR

Every node answers Ethereum-style JSON-RPC 2.0 on POST /. It is a read layer: blocks, balances, nonces, receipts, logs and contract reads work with familiar tools. Writing does not: there is no eth_sendRawTransaction. Signed transactions go to POST /tx (see the Node API Reference).

  • eth_blockNumber and "latest" blocks are the finalized height. Use the non-standard "tip" tag for the newest block.
  • State reads (eth_getBalance, eth_call, ...) read the tip unless you pass "finalized" or "safe". There are no historical state reads.
  • Addresses: the node accepts ase1 or 0x hex. Responses mix both. viem works as is; ethers v6 high-level block, transaction and receipt reads fail (see ethers and viem).
  • No WebSocket, no subscriptions, no mempool methods. Poll.

For in-block speed (showing a transaction before it is final) read Fast Confirmations. The REST routes are often simpler; see the Node API Reference.

Endpoint

URLNotes
https://testnet.asentum.comPOST /. Public gateway, about 25 requests a second per IP (burst 100).
https://explorer.asentum.comPOST / (GET / is the explorer website). Same node pool.
http://<your-node>:8545Your own node. Operator app: http://127.0.0.1:9545.
  • Chain id: 1423 (0x58f).
  • Batches: send an array of requests, get an array back in the same order. An empty array answers {"error":{"code":-32600,"message":"empty batch"}}.
  • Rate limits: the block methods (eth_getBlockByNumber, eth_getBlockByHash, eth_getBlockTransactionCountByNumber and ByHash) share the node's per-IP block-read budget with the REST block and receipt routes: a burst of 240, then 4 a second, one token per call inside a batch. Over the limit the node answers HTTP 429 with a JSON error and Retry-After; the gateway answers 429 with an HTML page. Details in Rate limits and caching.
  • CORS is open (Access-Control-Allow-Origin: *).
curl -s -X POST https://testnet.asentum.com -H 'content-type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"eth_blockNumber","params":[]}'
{"jsonrpc":"2.0","id":1,"result":"0x8b78c"}

Block tags: finalized vs tip

Asentum has two heads. The tip is the newest block on the best chain. The finalized head is a few blocks behind and never changes. (GET /v3/heads returns both.) Block tags map onto them like this:

TagBlock methodsState reads (eth_getBalance, eth_getTransactionCount, eth_call)
"latest"Finalized headTip
"pending"Finalized headTip. eth_getTransactionCount also adds your queued mempool transactions.
"tip" (non-standard)TipTip
"finalized", "safe"Finalized headFinalized state
"earliest"Block 0 (usually not stored, so null)Tip
"0x..." numberThat block. Above the finalized head: the pending block on the best chain, with finalized: false.Tip. The number is ignored: there are no historical state reads.
  • eth_blockNumber is the finalized height on the public RPC. Ethereum tools that count confirmations against it (ethers tx.wait(), viem waitForTransactionReceipt) therefore wait for finality. A node operator can set ASENTUM_RPC_ETH_LATEST=tip to make latest and eth_blockNumber follow the tip; the public gateway does not.
  • Blocks from the tip carry an extra field, finalized: false. eth_getBlockByNumber always adds finalized.
  • Mixing them is normal: eth_blockNumber can say 571,288 while eth_getBalance(..., "latest") already reflects block 571,291.

Addresses and encodings

Every address parameter takes an ase1 address or the 0x hex of the same 20 bytes. Responses are mixed, so convert before showing anything to a user:

FieldFormat
Transaction from, receipt from, block miner, log addressase1
Transaction to, receipt to, receipt contractAddress0x hex (null for a deploy's to)
Hashes, input, log data, topics0x hex
NumbersHex quantities (0x...), as in Ethereum
  • Hashes are BLAKE3, not Keccak. A block hash is BLAKE3 of the SSZ header; a transaction hash is BLAKE3 of the SSZ signed transaction.
  • Placeholders: v, r, s are zero (signatures are ML-DSA-65 and do not fit). logsBloom is all zeros, difficulty 0, sha3Uncles the empty-uncles constant, type always 0x2, effectiveGasPrice 0x1.
  • Gas and fees: the base fee is 1 wei per gas. gasPrice in a transaction is its maxFeePerGas.

Supported methods

This is the complete list. Anything else answers -32601 method not found.

MethodParamsReturns and notes
web3_clientVersion[]"asentum/v0.0.0-alpha.0". A fixed string, not the node release.
net_version[]"1423".
eth_chainId[]"0x58f".
eth_syncing[]Always false, even on a node that is catching up. Compare /v3/heads with the public RPC instead.
eth_accounts[]Always []. Nodes hold no user keys.
eth_blockNumber[]Finalized height (see Block tags).
eth_gasPrice[]The base fee of the finalized block: 0x1.
eth_maxPriorityFeePerGas[]Always 0x0.
eth_feeHistory[count, newest, percentiles?]Placeholder data: every base fee 0x1, every gasUsedRatio 0, every reward 0x0. count is capped at 1,024. Fine for wallets that need an answer; not real history.
eth_estimateGas[tx]Always 0x186a0 (100,000). It does not simulate. Contract calls need at least 50,000 gas to be admitted; heavy calls need more.
eth_getBalance[address, tag?]Balance in wei, read at the tip ("finalized"/"safe" for the finalized state).
eth_getTransactionCount[address, tag?]Nonce at the tip. "pending" adds this sender's queued transactions in this node's mempool: sign your next transaction with that.
eth_getBlockByNumber[tag | number, fullTx]Block, or null. Extra field finalized. "tip" for the newest block.
eth_getBlockByHash[hash, fullTx]Block, or null. Finalized blocks only, and no finalized field.
eth_getBlockTransactionCountByNumber[tag | number]Hex count, or null.
eth_getBlockTransactionCountByHash[hash]Hex count, or null.
eth_getTransactionByHash[hash]Transaction in an included block (tip blocks too), or null. Pending mempool transactions are not visible.
eth_getTransactionReceipt[hash, "finalized"?]Receipt with extra fields finalized and confirmations, or null. See below.
eth_getCode[address, tag?]The contract's JavaScript source as hex-encoded UTF-8 (not bytecode). "0x" for an account without code. The tag is ignored.
eth_getStorageAt[address, key, tag?]key is a contract storage key as a plain string (for example "self"), not a 32-byte slot. Returns the stored string as hex-encoded UTF-8, or "0x". The tag is ignored.
eth_call[{to, data}, tag?]Read-only contract call. data is hex-encoded JSON, not ABI. See below.
eth_getLogs[filter]Logs from finalized blocks. At most 10,000 blocks per call. See below.
eth_newFilter[filter]Filter id. Poll it with eth_getFilterChanges.
eth_newBlockFilter[]Filter id. eth_getFilterChanges returns new finalized block hashes.
eth_getFilterChanges[id]Logs or block hashes since the last poll.
eth_getFilterLogs[id]All logs for the filter's range.
eth_uninstallFilter[id]true or false.

Filters live in the memory of the node that created them: they expire after 5 minutes without a poll, at most 1,024 exist per node, and they are lost when the node restarts. Behind a load balancer, keep polling through the same host.

eth_getTransactionReceipt

Returns the receipt as soon as the transaction is in a block on the best chain, flagged with two extra fields. Pass "finalized" (or "safe") as a second param to get final receipts only.

curl -s -X POST https://testnet.asentum.com -H 'content-type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"eth_getTransactionReceipt","params":["0x3261a75d9804a1f92a08d88ca0878ad5edac0bf1e63c408d8f2181a6ffcd7ea1"]}'
{"jsonrpc":"2.0","id":1,"result":{
  "transactionHash":"0x3261a75d9804a1f92a08d88ca0878ad5edac0bf1e63c408d8f2181a6ffcd7ea1",
  "transactionIndex":"0x0",
  "blockHash":"0x5c21659e3aa85b139eece5ac1e7dca7f184695cd2ee377427ddd8e69f481e90d",
  "blockNumber":"0x8b774",
  "from":"ase1r3m77x6eyxt0emxg9jepm8ah7gy8kxmw90w5ty",
  "to":"0x63f640a9b98b6f28e06cb5cc0fce5d72b6c0eabd",
  "cumulativeGasUsed":"0x159fd","gasUsed":"0x159fd","contractAddress":null,
  "logs":[{"address":"ase1v0myp2de3dhj3crvkhxqlnjaw2mvp64a9efkpl",
           "topics":["0x81d0fb6aba18eb38f50c7301578cf6521aa9b049f0cd2ce926ffb8f51653727b"],
           "data":"0x7b2266726f6d223a...","logIndex":"0x0","removed":false, ...}],
  "logsBloom":"0x000...000","status":"0x1","effectiveGasPrice":"0x1","type":"0x2",
  "finalized":true,"confirmations":"0x8"}}
  • status is "0x1" (success) or "0x0" (failed). A failed one also has revertReason. (The REST /receipts route uses success: true/false and error instead.)
  • finalized: false means the block can still lose to a sibling at the same height. Then the receipt goes away (null), the transaction returns to the mempool and usually lands in a later block with a different blockNumber. Only finalized: true is permanent. confirmations is a hex quantity: tip minus block number, plus 1.
  • null means not in a block yet, or in a block this node does not have (nodes only keep history since their snapshot).

eth_call

Contracts are JavaScript, so there is no ABI. data is the hex of a UTF-8 JSON string {"method":"...","args":[...]}, and the result is the hex of the JSON-encoded return value. POST /view does the same thing without the hex, and is easier.

// {"method":"getPool","args":["3"]} as hex
curl -s -X POST https://testnet.asentum.com -H 'content-type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"eth_call","params":[{"to":"ase1v0myp2de3dhj3crvkhxqlnjaw2mvp64a9efkpl","data":"0x7b226d6574686f64223a22676574506f6f6c222c2261726773223a5b2233225d7d"},"latest"]}'
{"jsonrpc":"2.0","id":1,"result":"0x227b5c2269645c223a5c22335c222c..."}
const data = '0x' + Buffer.from(JSON.stringify({ method: 'getPool', args: ['3'] })).toString('hex');
const res = await fetch('https://testnet.asentum.com', {
  method: 'POST',
  headers: { 'content-type': 'application/json' },
  body: JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'eth_call', params: [{ to: 'ase1v0myp2de3dhj3crvkhxqlnjaw2mvp64a9efkpl', data }, 'latest'] }),
});
const { result } = await res.json();
const value = JSON.parse(Buffer.from(result.slice(2), 'hex').toString()); // the method's return value
  • Reads the tip. "finalized" or "safe" read the finalized state. Block numbers are ignored.
  • from, value and gas in the call object are ignored.
  • Address arguments in args are passed through unchanged. Contracts store accounts as lowercase 0x hex, so pass the 0x form.
  • ABI-encoded data (for example 0x70a08231...) fails with -32602 data field must be hex-encoded JSON {method, args}. Empty data returns "0x".
  • A method that throws, or a method that does not exist, fails with -32603 eth_call failed: ... and the contract's message, for example eth_call failed: contract method 'unbond' failed: not a bonded validator.

eth_getLogs and filters

Contract events become logs. address is the contract (ase1), topics[0] is BLAKE3 of the event name (not Keccak of a Solidity signature), there are no further topics, and data is the hex of the event's JSON payload.

curl -s -X POST https://testnet.asentum.com -H 'content-type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"eth_getLogs","params":[{"fromBlock":"0x8b784","toBlock":"0x8b798","address":"ase1v0myp2de3dhj3crvkhxqlnjaw2mvp64a9efkpl","topics":["0x055073c2b735a03c49665d6df1f3c03dbd639aaf4c06fbdb8de121db2a981ab4"]}]}'
{"jsonrpc":"2.0","id":1,"result":[{"address":"ase1v0myp2de3dhj3crvkhxqlnjaw2mvp64a9efkpl",
  "topics":["0x055073c2b735a03c49665d6df1f3c03dbd639aaf4c06fbdb8de121db2a981ab4"],
  "data":"0x7b22706f6f6c4964223a2235222c2270617965...","blockNumber":"0x8b784",
  "transactionHash":"0x32a3ffd2705658a4252c1b9ed29573df34c759e2c9ff42be7c7362d8f22c0dc2","transactionIndex":"0x2",
  "blockHash":"0xf823465b455c652a6a0cda2ee9b91b35822e9a11bc12b117fa3e4e94223ba40b","logIndex":"0x1","removed":false}, ...]}
  • To filter on an event, compute BLAKE3 of the UTF-8 bytes of its name and put it in topics[0]. Swap is 0x055073c2...981ab4 (the example above) and Transfer is 0x81d0fb6a...53727b. Decode data with JSON.parse of the UTF-8 bytes.
  • Finalized blocks only. Logs from blocks above the finalized head are never returned, so a log poller runs a few blocks behind the tip. Tags like "tip" are not accepted in a filter.
  • At most 10,000 blocks per call (inclusive). A wider range fails with -32602 log scan: requested range N exceeds 10000-block limit. Heights this node does not store are skipped, not reported.
  • logIndex counts across the whole block, so (blockHash, logIndex) is unique.
  • Defaults: fromBlock and toBlock are "latest" (the finalized head).

Not supported

These answer -32601 method not found on every node:

MethodUse instead
eth_sendRawTransaction, eth_sendTransactionPOST /tx with {"rawTx":"0x..."} (signed by a native wallet, the SDK or @asentum/connect).
eth_sign, eth_signTransaction, personal_*Sign in the wallet. Nodes hold no keys.
eth_subscribe, eth_unsubscribe (WebSocket)Poll. There is no WebSocket endpoint; /ws answers 404.
txpool_content, txpool_status, eth_pendingTransactionsNone. There is no way to list the mempool. /balance pendingNonce and eth_getTransactionCount "pending" show your own queued count.
eth_getRawTransactionByHasheth_getTransactionByHash, or GET /block/<n> for decoded transactions.
eth_getBlockReceiptsGET /block/<n>/receipts.
eth_getProof, eth_createAccessList, debug_*, trace_*None.
asentum_* (any name, for example asentum_viewCall)There are no asentum_* methods. Use POST /view and the other REST routes.

Errors

CodeWhen
-32600method is not a string, or an empty batch.
-32601Unknown or unsupported method.
-32602Bad params: wrong type, bad hash length, bad block tag, eth_call data that is not JSON, log range over 10,000 blocks, unknown filter id.
-32603eth_call failed (contract error or missing method), filter store full, or any other server error. The message says why.
  • A body that is not valid JSON does not get a JSON-RPC error: the node answers HTTP 500 {"error":"internal","message":"invalid JSON body: ..."}.
  • Codes -32000 and -32010 are never used.
  • Rate limits are HTTP 429, not a JSON-RPC error.

ethers and viem

Tested with viem 2.48 and ethers 6.16 against the public RPC.

Callviemethers v6
Block number, chain idWorksWorks
Balance of a 0x addressWorksWorks
Balance of an ase1 addressType check fails: pass the 0x formFails (treated as an ENS name): use provider.send
getBlock, getTransaction, getTransactionReceiptWorksFails: "invalid address" on the ase1 miner or from field
getFeeDataWorksFails (reads a block)
getLogsWorksFails on the ase1 log address: use provider.send
call (eth_call)Works (data is hex JSON, see eth_call)Works
Sending a transactionNot possibleNot possible
// viem: read-only works as is
import { createPublicClient, http } from 'viem';
const client = createPublicClient({ transport: http('https://testnet.asentum.com') });
const finalized = await client.getBlockNumber();             // finalized height
const receipt = await client.getTransactionReceipt({ hash }); // receipt.status === 'success'

// ethers v6: use provider.send for anything that returns a block, tx, receipt or log
import { ethers } from 'ethers';
const provider = new ethers.JsonRpcProvider('https://testnet.asentum.com', 1423, { staticNetwork: true });
const height = await provider.getBlockNumber();
const r = await provider.send('eth_getTransactionReceipt', [hash]);
const ok = r && r.status === '0x1';
const balance = BigInt(await provider.send('eth_getBalance', ['ase1...', 'latest']));
  • Both libraries wait for finality in their wait helpers, because eth_blockNumber is the finalized height. For in-block speed, poll the receipt yourself (see Fast Confirmations).
  • MetaMask and other EVM wallets can show the network but cannot sign Asentum transactions. See Ethereum Compatibility.
  • For signing and sending, use the SDK or @asentum/connect.

Read next