Bittensor is a Substrate-based network whose primary JSON-RPC interface is the subtensor RPC, used for staking, subnet management, and metagraph queries. In addition to that surface, Bittensor exposes an EVM-compatible JSON-RPC layer that follows the Ethereum method vocabulary, allowing tools such as ethers.js, viem, and web3.js to interact with EVM-style balances and contracts on TAO. The two surfaces are distinct: the EVM layer does not expose subnet or staking data, and method availability, precompiles, and chain ID parameters vary by RPC provider. This guide explains the architectural split, shows a runnable ethers.js example that calls eth_chainId, eth_blockNumber, and eth_getBalance, and provides a reproducible results table so you can verify your own endpoint. Always confirm chain ID and endpoint parameters against current Bittensor documentation rather than assuming Ethereum defaults.
The Architectural Split Between Subtensor RPC and the EVM Layer
Bittensor is built on Substrate, and its native JSON-RPC interface is the subtensor RPC. That surface uses Substrate-style method namespaces for staking, subnet registration, metagraph data, and other chain-native operations. If you are coming from Polkadot or Kusama tooling, the subtensor RPC will feel familiar: it is the canonical way to read and write Bittensor-specific state.
Alongside that, Bittensor exposes an EVM-compatible JSON-RPC surface. This layer mirrors the method vocabulary defined in the Ethereum JSON-RPC specification, so calls like eth_chainId, eth_blockNumber, and eth_getBalance behave in the way Ethereum tooling expects. The EVM layer is not a replacement for the subtensor RPC; it is a parallel interface that lets Ethereum-native tools talk to the EVM portion of the network.
The practical consequence is that a single Bittensor node or provider endpoint may serve one or both surfaces. Some public endpoints expose only the subtensor RPC, some expose only the EVM JSON-RPC, and some expose both. Before you write integration code, confirm which surface your endpoint serves. The Bittensor RPC node guide covers the subtensor side in depth, while this article focuses on the EVM layer.
- Subtensor RPC: Substrate-style methods for staking, subnets, and metagraph data.
- EVM JSON-RPC: Ethereum-style methods for chain ID, blocks, balances, and EVM contracts.
- Endpoint scope varies: some providers serve one surface, some serve both.
Why an EVM Layer Matters for Bittensor Tooling
The EVM layer lowers the integration cost for teams already using Ethereum tooling. Instead of learning Substrate-specific method names and type encoding, a developer can point ethers.js, viem, or web3.js at a Bittensor EVM JSON-RPC endpoint and use the same patterns they use on Ethereum. Wallet 'add network' flows also become straightforward: the wallet needs an RPC URL, a chain ID, and a currency symbol, all of which the EVM layer can supply.
This matters for contract deployment and EVM-style balance reads on TAO. If your application logic is written against the EVM, you can reuse it on Bittensor without rewriting the transport layer. The ethers.js documentation describes the standard connection pattern for any EVM-compatible JSON-RPC endpoint, and that pattern applies here.
However, the EVM layer is only part of the network. Substrate-native concepts such as subnets, alpha tokens, and staking live outside the EVM namespace. If your application needs metagraph data or staking operations, you will need the subtensor RPC. The Bittensor subtensor staking read path article covers that read path in detail.
- Reuse Ethereum tooling without learning Substrate specifics.
- Enable EVM-style contracts and balance reads on TAO.
- Keep subnet and staking logic on the subtensor RPC.
How a Wallet Sees Bittensor Through the EVM Lens
When you add Bittensor to an EVM-compatible wallet such as MetaMask, the wallet needs three pieces of information: an RPC URL, a chain ID, and a currency symbol. The RPC URL must point to an endpoint that serves the EVM JSON-RPC surface. The chain ID must match the Bittensor EVM chain ID, which you should confirm from current Bittensor documentation rather than assuming a value. The currency symbol is typically TAO, with the smallest unit being RAO.
The wallet will then use standard EVM methods to display balances and send transactions. It does not know about subnets or staking; those remain outside the wallet's EVM view. If you also need to interact with the subtensor RPC, you will use a separate tool or endpoint. The Bittensor RPC guide (RPC Assistant) can help you identify the correct endpoint for each surface.
A common pitfall is using a subtensor-only endpoint as the wallet RPC URL. The wallet will fail to connect because the endpoint does not serve eth_* methods. Always verify that your chosen endpoint responds to eth_chainId before adding it to a wallet.
- Wallet needs: RPC URL, chain ID, currency symbol.
- Confirm chain ID from current Bittensor docs; do not assume.
- Subtensor-only endpoints will not work as wallet RPC URLs.
Runnable Node.js Example: Connecting with ethers.js
The following Node.js script uses ethers.js to connect to a Bittensor EVM JSON-RPC endpoint. It calls eth_chainId and eth_blockNumber, then reads the balance of a given address. Replace the RPC_URL and ADDRESS constants with your own values. The script prints the chain ID, current block number, and balance, confirming that the endpoint is serving the EVM surface.
This example follows the standard ethers.js connection pattern documented at docs.ethers.org. If the endpoint does not serve EVM methods, the provider call will throw an error, which is itself a useful diagnostic.
// bittensor-evm-check.js
// Run with: node bittensor-evm-check.js
const { ethers } = require("ethers");
// Replace with your Bittensor EVM JSON-RPC endpoint
const RPC_URL = "https://your-bittensor-evm-endpoint";
// Replace with an address you want to inspect
const ADDRESS = "0x0000000000000000000000000000000000000000";
async function main() {
const provider = new ethers.JsonRpcProvider(RPC_URL);
const chainId = await provider.send("eth_chainId", []);
console.log("eth_chainId:", chainId);
const blockNumber = await provider.getBlockNumber();
console.log("eth_blockNumber:", blockNumber);
const balance = await provider.getBalance(ADDRESS);
console.log("eth_getBalance:", balance.toString());
}
main().catch((err) => {
console.error("EVM JSON-RPC check failed:", err.message);
process.exit(1);
});Runnable curl Example: Raw JSON-RPC Requests
If you prefer to inspect the raw JSON-RPC responses without a library, curl works well. The following commands send eth_chainId and eth_blockNumber to your endpoint. Replace the URL with your Bittensor EVM JSON-RPC endpoint. The response should be a JSON object with a result field.
These raw calls are useful for troubleshooting because they show the exact request and response, including any error objects. If you receive a method-not-found error, the endpoint likely does not serve the EVM surface.
# Replace with your Bittensor EVM JSON-RPC endpoint
RPC_URL="https://your-bittensor-evm-endpoint"
# eth_chainId
curl -s -X POST "$RPC_URL" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"eth_chainId","params":[],"id":1}'
# eth_blockNumber
curl -s -X POST "$RPC_URL" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":2}'Reproducible Results Table for Your Own Endpoint
Because EVM method availability and chain parameters vary by RPC provider, you should measure against your own endpoint rather than relying on generic values. Use the table below to record what you observe. The 'Expected' column describes the general shape of a successful response, not a specific value. Fill in the 'Observed' column with the actual output from your endpoint.
Run each method at least twice to confirm consistency. If a method returns an error, record the error message in the Notes column. This table becomes a reproducible record of your endpoint's EVM surface.
- Method: eth_chainId | Expected: hex string chain ID | Observed: [fill] | Endpoint: [fill] | Notes: [fill]
- Method: eth_blockNumber | Expected: hex string block number | Observed: [fill] | Endpoint: [fill] | Notes: [fill]
- Method: eth_getBalance | Expected: hex string balance in RAO | Observed: [fill] | Endpoint: [fill] | Notes: [fill]
- Method: eth_getBlockByNumber | Expected: block object or null | Observed: [fill] | Endpoint: [fill] | Notes: [fill]
- Method: net_version | Expected: network ID string | Observed: [fill] | Endpoint: [fill] | Notes: [fill]
Limitations and Tradeoffs of the Bittensor EVM Layer
The EVM layer is not a full Ethereum equivalent. Method availability differs from Ethereum, and precompiles may not be present or may behave differently. Some methods that work on Ethereum may return errors or unsupported responses on Bittensor. This is documented behavior that varies by provider, so you should test the specific methods your application needs.
The EVM layer does not expose subnet or staking data. If your application needs metagraph information, alpha token balances, or staking operations, you must use the subtensor RPC. The Bittensor subtensor staking read path article explains how to read that data.
Chain ID and endpoint parameters must be confirmed against current Bittensor documentation rather than assumed. Some public endpoints serve only one of the two surfaces, so you may need separate endpoints for EVM and subtensor operations. When comparing protocol choices, the JSON-RPC vs gRPC vs REST article provides context on transport tradeoffs.
- Method availability and precompiles differ from Ethereum and vary by provider.
- No subnet or staking data through EVM methods.
- Chain ID and endpoint parameters must be confirmed from current docs.
- Some endpoints serve only one surface.
Troubleshooting Common EVM JSON-RPC Connection Issues
If your ethers.js script fails with a connection error, first verify that the RPC URL is correct and reachable. A subtensor-only endpoint will not respond to eth_chainId. Try the curl example above to see the raw response. If you receive a method-not-found error, the endpoint does not serve the EVM surface.
If the chain ID returned does not match what you expected, do not assume it is wrong. Confirm the current Bittensor EVM chain ID from the Bittensor documentation. Chain IDs can change or differ between testnet and mainnet. Similarly, if balance values look unusual, remember they are denominated in RAO, the smallest unit of TAO, not TAO itself.
If a specific method fails while others succeed, that method may not be supported by your provider. Record it in your results table and check whether an alternative method or provider is needed. For a broader reference on EVM method vocabulary, see the Monad JSON-RPC methods reference, which covers the same Ethereum-style method set.
- Verify RPC URL is reachable and serves EVM methods.
- Method-not-found indicates a subtensor-only endpoint.
- Confirm chain ID from current Bittensor docs.
- Balance is in RAO, not TAO.
Next Steps for Building on Bittensor EVM
Once you have verified that your endpoint serves the EVM surface, you can integrate it into your application. Use ethers.js, viem, or web3.js as you would on Ethereum. For contract deployment, ensure your toolchain is configured with the correct chain ID and RPC URL. Test on a testnet before mainnet if available.
For production workloads, consider a managed RPC provider that offers both surfaces. The OnFinality Bittensor network page describes the available endpoints and services. You can also review RPC pricing and the API service for scale and support options.
To deepen your understanding of the subtensor side, read the Bittensor RPC node guide and the Bittensor subtensor staking read path. For a broader overview of OnFinality Learn resources, visit the OnFinality Learn hub.
- Integrate verified EVM endpoint into your app.
- Use managed providers for production scale.
- Read subtensor guides for non-EVM operations.