Hermoddocs

Tron

Calling the Tron Mainnet RPC gateway — three endpoint surfaces, when to use each, and code samples for TronWeb / ethers.js / cURL.

Tron is different from Ethereum: instead of one JSON-RPC URL per chain, Tron exposes three endpoint surfaces that serve different use cases. Each has its own URL on the gateway, its own caching profile, and its own per-method CU weights.

This page covers what each surface does, when to use it, and how to call it from common libraries.

The 3 surfaces

SurfacePath prefixWhat it serves
Full Node/v1/tron/<network>/wallet/Latest state + writes. Most dapps live here.
Solidity Node/v1/tron/<network>/walletsolidity/Reads against irreversible (~60s lag) data. Cache-friendly.
JSON-RPC/v1/tron/<network>/jsonrpcEVM-compat subset (eth_*). For Ethereum-ported code.

<network> is one of mainnet (Tron Mainnet) or nile (Tron Nile testnet). Mainnet examples below.

When to use which

Full Node is the default. It serves the freshest data and accepts broadcasttransaction writes. TronWeb's tronWeb.trx.getCurrentBlock(), getAccount(), triggerConstantContract() all hit this surface.

Solidity Node mirrors the full node but only exposes data that's already irreversible (typically ~60 seconds old, after 2/3 SR confirmations). The big payoff: confirmed data caches longer, so reads hit our Redis cache more often → faster responses and lower CU spend. Use this for historical queries, indexing, anything that doesn't need the latest 60 seconds.

JSON-RPC is a subset of the Ethereum eth_* API. If you're porting an existing Ethereum dapp and want to keep your ethers.js / web3.js provider code, point it here. Subset: no eth_subscribe (no WebSocket on Tron's RPC layer), no debug_*, no trace_*, no stateful filters (eth_newFilter etc). See the whitelist.

Quick start

TronWeb (Full Node)

import TronWeb from "tronweb";
 
const tronWeb = new TronWeb({
  fullHost: "https://infra.originstake.com/v1/tron/mainnet",
  headers: { "X-OriginRPC-Token": process.env.ORIGINRPC_KEY! },
});
 
const block = await tronWeb.trx.getCurrentBlock();
console.log(block.block_header.raw_data.number);

TronWeb (Solidity Node, cheaper reads)

import TronWeb from "tronweb";
 
const tronWebSolidity = new TronWeb({
  fullHost: "https://infra.originstake.com/v1/tron/mainnet",
  solidityNode: "https://infra.originstake.com/v1/tron/mainnet",
  headers: { "X-OriginRPC-Token": process.env.ORIGINRPC_KEY! },
});
 
// Reads go to the solidity node when you call the .trx.solidity.* methods.
const acct = await tronWebSolidity.trx.getAccount("TLsV52sRDL79HXGGm9yzwKibb6BeruhUzy");

ethers.js (JSON-RPC)

import { JsonRpcProvider } from "ethers";
 
const provider = new JsonRpcProvider(
  "https://infra.originstake.com/v1/tron/mainnet/jsonrpc",
  undefined,
  { staticNetwork: true },
);
 
const blockNum = await provider.getBlockNumber();

Pass your key via fetch options if needed:

import { FetchRequest, JsonRpcProvider } from "ethers";
 
FetchRequest.registerGetUrl(FetchRequest.createGetUrlFunc({
  headers: { "X-OriginRPC-Token": process.env.ORIGINRPC_KEY! },
}));

cURL

# Full Node — latest block
curl -X POST https://infra.originstake.com/v1/tron/mainnet/wallet/getnowblock \
  -H "Content-Type: application/json" \
  -H "X-OriginRPC-Token: $ORIGINRPC_KEY" \
  -d '{}'
 
# Solidity Node — historical account
curl -X POST https://infra.originstake.com/v1/tron/mainnet/walletsolidity/getaccount \
  -H "Content-Type: application/json" \
  -H "X-OriginRPC-Token: $ORIGINRPC_KEY" \
  -d '{"address":"TLsV52sRDL79HXGGm9yzwKibb6BeruhUzy","visible":true}'
 
# JSON-RPC — Ethereum-style
curl -X POST https://infra.originstake.com/v1/tron/mainnet/jsonrpc \
  -H "Content-Type: application/json" \
  -H "X-OriginRPC-Token: $ORIGINRPC_KEY" \
  -d '{"jsonrpc":"2.0","method":"eth_blockNumber","id":1}'

Authentication

Same as the EVM endpoints. Three accepted forms in priority order:

  1. Header: X-OriginRPC-Token: <key> (recommended)
  2. Query: ?api=<key> or ?secret=<key>
  3. Path: leading path segment matching originrpc_live_… or os_live_…

A request with no key falls into the anonymous tier (60 RPS per IP, 20k requests per IP per day; broadcast methods hard-denied). See rate limits for the full table.

Pricing

Tron CU weights are calibrated so the Pro plan ($29/mo, 300M CU) absorbs about 20M Tron requests a month at a typical mix (~15 CU per request).

Representative weights:

MethodCU
wallet/getnowblock1
wallet/getaccount5
wallet/triggerconstantcontract20
walletsolidity/getaccount5
walletsolidity/gettransactioninfobyid15
eth_blockNumber1
eth_chainId1
eth_call20
eth_getLogs (≤1k blocks)40
eth_getLogs (>10k blocks)150 - 300 (dynamic)
wallet/broadcasttransaction80
eth_sendRawTransaction80

Full table: see the admin method-weights view (admin-only) or query /public/method-weights?architecture=tron (public, read-only).

See billing for how CU is converted to USD.

Error handling

Tron's API has a quirk: many failures return HTTP 200 with an error field in the body. The gateway parses these and classifies them in metrics as tron_error (not 2xx) so dashboards reflect actual success rates. Clients see the raw upstream body — same shape Tron itself returns.

The five error shapes the gateway recognizes:

// 1. Explicit Error string (e.g. bad input)
{ "Error": "class org.tron.core.services.http.JsonFormat$ParseException : 1:12: INVALID hex String" }
 
// 2. result:false + code (validation failure)
{ "result": false, "code": "SIGERROR", "message": "..." }
 
// 3. result:false on broadcast (mempool admit failure)
{ "result": false, "txid": "...", "code": "BANDWITH_ERROR" }
 
// 4. Empty body 200 (account not found)
{}
 
// 5. JSON-RPC standard error envelope
{ "jsonrpc": "2.0", "id": 1, "error": { "code": -32601, "message": "..." } }

Credit charge behavior: validation failures (SIGERROR etc) charge 50% of method weight — the upstream still did CPU work. Bandwidth / broadcast-admit failures charge 100% — the tx was verified, the failure was downstream. Upstream 5xx / timeouts charge 0% (our problem, not yours).

JSON-RPC whitelist

The /jsonrpc endpoint forwards only the methods Tron actually implements. Unknown methods get a JSON-RPC -32601 error with a helpful hint.

Supported (~20 methods):

eth_chainId, eth_blockNumber, net_version, web3_clientVersion, eth_getBlockByNumber, eth_getBlockByHash, eth_getBlockTransactionCountByNumber, eth_getBlockTransactionCountByHash, eth_getTransactionByHash, eth_getTransactionByBlockNumberAndIndex, eth_getTransactionByBlockHashAndIndex, eth_getTransactionReceipt, eth_getBalance, eth_getCode, eth_getStorageAt, eth_getTransactionCount, eth_call, eth_estimateGas, eth_gasPrice, eth_getLogs, eth_sendRawTransaction.

Explicitly denied with a friendly message:

eth_subscribe, eth_unsubscribe (no WebSocket on Tron's RPC), eth_getProof, eth_newFilter + friends (no stateful filters), eth_pendingTransactions, eth_sign, eth_sendTransaction, debug_*, trace_*.

If you hit "unknown method" for something you expected, ping us — we add methods as Tron's RPC implementation grows.

Caching

Each method has a TTL configured by admin. The gateway uses two layers:

  1. Single-flight dedup (in-process): concurrent calls for the same key collapse to one upstream call. Especially valuable for wallet/getnowblock and eth_blockNumber — polling clients amplify into one upstream request per second total.
  2. Redis cache (cross-instance): TTL-bound. Skipped for HTTP 4xx+, bodies > 1MB, or responses our error classifier flagged as tron_error / jsonrpc_error.

Responses carry an X-OGS-Cache: HIT or MISS header so you can verify locally.

The cache TTL by method group:

GroupExample methodsTTL
Hotgetnowblock, getaccount, eth_blockNumber0 - 3s
Hot blockgetblockbynum, eth_getBlockByNumber60s
Finalwalletsolidity/getaccount, eth_getTransactionReceipt30 - 300s
Writebroadcasttransaction, eth_sendRawTransaction0 (no cache)

Rate limits

Per-IP for anonymous traffic, per-key for authenticated:

TierRPSNotes
Anonymous60 per IP+ 15 RPS sub-cap on getnowblock / eth_blockNumber; broadcasts hard-denied
Free100Same plan as EVM
Pro500
Scale1,500

See rate limits for headers + retry guidance.

Status

Per-endpoint-kind health is published on the public status page. A Tron chain that has a healthy Full Node but a degraded Solidity Node will show overall degraded with a Solidity-specific indicator — you can keep traffic flowing through Full while the solidity surface recovers.

On this page