npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

tron-grpc

v1.0.1

Published

A TypeScript client for interacting with the TRON blockchain via gRPC

Downloads

1,801

Readme

tron-grpc

A fully-typed TypeScript client for the TRON blockchain that talks to a java-tron full node over gRPC.

Developed by TronSave and SaveWallet.

The public API takes ordinary, human inputs and returns decoded, readable values — you never touch a Buffer, a base58 codec, or a sun encoding:

  • Addresses in, as base58 (T…) or hex (41… / 0x41…).
  • Amounts in, as decimal TRX (number | string | bigint).
  • Out, base58 addresses and exact numeric strings — never raw bytes.

All base58 ⇄ 21-byte conversion, amount ⇄ sun encoding, transaction signing, and protobuf packing happen inside the library.


HomePage

Questions or feedback are welcome here.


Contents


Install

npm install tron-grpc

Requires Node.js 20+ (uses the built-in fetch and global TextEncoder).


Quick start

import { TronGrpcClient } from 'tron-grpc';

const client = TronGrpcClient.fromNetwork('mainnet', {
  headers: { 'TRON-PRO-API-KEY': process.env.TRON_PRO_API_KEY ?? '' },
});

// Latest block
const head = await client.getNowBlock();
console.log(head.number, head.hash); // 83259967  0000000004f6...

// Account balance (TRX), as both sun and decimal
const account = await client.getAccount('TWd4WrZ9wn84f5x1hZhL4DHvk738ns5jwb');
console.log(account.balanceSun); // "11223430628613"
console.log(account.balanceTrx); // "11223430.628613"

// USDT (TRC20) balance
const usdt = await client.getTrc20Balance(
  'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t', // USDT contract
  'TWd4WrZ9wn84f5x1hZhL4DHvk738ns5jwb',
);
console.log(usdt.raw, usdt.value); // "1500000000000000" "1500000000"

client.close();

API

Client

Create a client by network or by explicit host:

const client = TronGrpcClient.fromNetwork('mainnet'); // 'mainnet' | 'nile' | 'shasta'
// or
const client = new TronGrpcClient('grpc.trongrid.io:50051', {
  headers: { 'TRON-PRO-API-KEY': '…' },              // added to every call's metadata
  grpcOptions: { 'grpc.keepalive_time_ms': 30000 },  // merged over tuned defaults
});

| Method | Returns | Notes | | --- | --- | --- | | getNowBlock() | BlockSummary | Current head block. | | getBlockByNum(num) | BlockSummary | Block by height. | | getAccount(address) | TronAccount | balanceSun + balanceTrx; exists flag. | | getBalance(address) | string | TRX balance as a decimal string. | | getAccountResources(address) | AccountResources | Bandwidth + energy. | | getTransactionById(txid) | TransactionResult | Decoded tx (found flag). | | getTransactionInfoById(txid) | TransactionInfoResult | Receipt: block, fee, result. | | getTrc20Balance(contract, owner, decimals?) | Trc20Balance | balanceOf via constant call. | | triggerConstantContract(input) | ConstantCallResult | Read-only contract call. | | triggerContract(input) | object | Unsigned state-changing call (TransactionExtention). | | estimateEnergy(input) | { energyRequired } | Energy estimate. | | createTransaction(from, to, amountTrx) | object | Unsigned TRX transfer. | | signAndBroadcast(extention, privateKey) | BroadcastResult | Sign + broadcast. | | sendTrx({ from?, to, amount, privateKey }) | BroadcastResult | Create → sign → broadcast. | | getChainParameters() | object | Chain parameters (normalized). | | request<T>(method, message) | Promise<T> | Escape hatch for any Wallet RPC. | | isReady / close() | boolean / void | Connectivity flag / shutdown. |

All addresses passed to any method may be base58 or hex; all addresses returned are base58. All *Sun / balance / fee fields are exact integer strings.

Utilities

import {
  // addresses
  isAddress, toBase58Address, toHexAddress, base58ToBytes, bytesToBase58Address,
  // units (bigint-safe, no float)
  trxToSun, sunToTrx, toBaseUnits, fromBaseUnits, SUN_PER_TRX,
  // crypto
  privateKeyToAddress, fromMnemonic, signMessage, signTransactionId,
  // ABI / hex
  encodeFunctionData, encodeAddressParam, decodeUint256, functionSelector,
  stripHexPrefix, hexToBytesSafe,
} from 'tron-grpc';

trxToSun('1.5');              // 1500000n
sunToTrx('11223430628613');   // "11223430.628613"
trxToSun('1.1234567');        // throws — exceeds TRX's 6 decimals

isAddress('TWd4WrZ9wn84f5x1hZhL4DHvk738ns5jwb');    // true (checksum-validated)
toHexAddress('TWd4WrZ9wn84f5x1hZhL4DHvk738ns5jwb'); // "41e3...."

privateKeyToAddress('a5a5…57e3'); // "TN3EwEhs2fHaCu55srzPNanWxJjEtQUPBC"
const { privateKey, address } = fromMnemonic(mnemonic, "m/44'/195'/0'/0/0");

Sending TRX

sendTrx performs CreateTransaction2 → local secp256k1 signing → BroadcastTransaction:

const result = await client.sendTrx({
  to: 'TBBjHvgPQ9q4SNs9rXoakYkYRjgJZPaZcu',
  amount: '1.5',                              // TRX (number | string | bigint)
  privateKey: process.env.TRON_PRIVATE_KEY!,  // hex, with or without 0x
  // from is optional — derived from the private key when omitted
});

console.log(result.success, result.txid, result.code);

For full control you can split the steps:

const ext = await client.createTransaction(from, to, '1.5'); // unsigned
const res = await client.signAndBroadcast(ext, privateKey);  // sign + broadcast

The node-built raw_data is re-broadcast verbatim and the node-computed txid is what gets signed, so the signature matches java-tron's exact serialization (verified by a successful testnet broadcast in the test suite).


TRC20 balances

getTrc20Balance ABI-encodes balanceOf(address), calls TriggerConstantContract, and decodes the uint256:

const { raw, value, decimals } = await client.getTrc20Balance(usdtContract, holder, 6);

For arbitrary calls, use triggerConstantContract with either a functionSelector + ABI-encoded params, or pre-encoded data:

const res = await client.triggerConstantContract({
  ownerAddress: holder,
  contractAddress: usdtContract,
  functionSelector: 'balanceOf(address)',
  params: encodeAddressParam(holder),
});
res.constantResult[0]; // hex word

Environment variables

Copy .env.example to .env and fill it in. The recommended way to supply secrets is via the environment, never in code:

| Var | Used by | Purpose | | --- | --- | --- | | TRON_PRO_API_KEY | client + tests | TronGrid API key (raises rate limits), sent as the TRON-PRO-API-KEY gRPC metadata header. | | TRON_PRIVATE_KEY | broadcast test | Testnet private key (hex, 64 chars). | | TRON_TO | broadcast test | Recipient address for the test transfer. | | TRON_NETWORK | broadcast test | nile or shasta (the test refuses mainnet). |


Running the tests

npm run typecheck   # tsc --noEmit (sources + test.ts), full strict mode
npm run lint        # eslint (no `any` in public API)
npm test            # tsx test.ts — hits the real network
npm run verify      # all three

The suite makes real calls (nothing is hardcoded to pass) and includes four Tronscan cross-checks — for the same data, it reads via this library (gRPC) and via Tronscan's public REST API and asserts the values are equal:

  1. TRX balance of an example address — gRPC getAccount vs /api/account.
  2. TRC20 (USDT) balance — gRPC triggerConstantContract vs /api/account.
  3. Block by number (hash, witness, timestamp) — gRPC getBlockByNum vs /api/block.
  4. Transaction by txid (block, result) — gRPC getTransactionInfoById vs /api/transaction-info.

Cross-checks run on mainnet (Tronscan's public API is mainnet). Signing + broadcast runs on the Nile testnet only — it signs a 1-sun transfer, asserts the node accepts it, and confirms it on-chain. It never spends mainnet TRX.


Security

  • Never commit secrets. test.ts (which holds a key + endpoint) and .env are listed in .gitignore. Only .env.example is committed.
  • Supply the private key and API key via environment variables (see above); the committed fallbacks exist only for the original throwaway test account.
  • The broadcast test is hard-coded to refuse mainnet and only runs against Nile/Shasta, so a misconfigured key can never spend real TRX.

Project structure

src/
  client/                 the gRPC client, split by feature for easy maintenance
    core.ts               transport: connection, request<T>(), buildExtention/query helpers
    helpers.ts            pure helpers (sun/int, assert, decode, trigger builder)
    blocks.ts             block reads
    accounts.ts           account reads + account-management builders
    transactions.ts       tx reads + create → sign → broadcast
    contracts.ts          smart-contract calls, deploy, TRC20
    resources.ts          staking / delegation (Stake 2.0)
    witnesses.ts          witness management + voting
    assets.ts             TRC10 issuance / transfer / queries
    governance.ts         proposals, exchanges, storage, market
    chain.ts              node / chain info
    shielded.ts           zk-SNARK (shielded) RPCs
    index.ts              composes the chain → `TronGrpcClient` + `fromNetwork`
  codecs/                 abi.ts (encode/decode), decode.ts (response → readable)
  utils/                  address.ts, units.ts, crypto.ts, hex.ts
  proto/                  vendored TRON .proto + generated .ts
  network.ts              TRON_NETWORKS
  protoLoader.ts          single proto-loader package definition
  types.ts                public typed shapes
  index.ts                public barrel
index.ts                  package root barrel (re-exports src + compat aliases)

The client is one flat class (client.getAccount(...), client.freezeBalanceV2(...)) assembled from the feature files through a single inheritance chain (core → blocks → accounts → … → shielded → TronGrpcClient), so each area lives in its own small file while the public API stays flat.

Design: why gRPC + proto-loader

This library uses @grpc/grpc-js + @grpc/proto-loader with the official TRON .proto definitions (vendored under src/proto/).

  • proto-loader loads the .proto files at runtime and produces plain request objects — no codegen step is required to call an RPC, and adding a new method is a one-liner via the typed request<T>() escape hatch.
  • longs: String keeps 64-bit integers (balances, amounts) exact — they surface as decimal strings instead of lossy JS numbers.
  • The hand-written typed layer (src/codecs, src/types.ts) decodes those raw responses into readable objects, so any never reaches the public API.

The alternative, ts-proto, would generate a typed runtime but require a build step and a transport rewrite; the generated message classes are already vendored here for reference, but the transport deliberately stays on the loader-based path for flexibility.


Implemented methods

The client covers essentially the whole Wallet gRPC service (147 RPCs). Each method takes human inputs (base58/hex addresses, decimal TRX) and returns readable, bytes-free results; state-changing builders return an unsigned TransactionExtention that you pass to signAndBroadcast. Anything not given a named method is still reachable through the typed request<T>() escape hatch.

  • Blocks: getNowBlock, getBlockByNum, getBlockById, getBlockByLatestNum, getBlockByLimitNext, getBlock, getTransactionCountByBlockNum, getBlockBalanceTrace.
  • Accounts: getAccount, getBalance, getAccountResources, getAccountById, getAccountNet, getAccountBalance, updateAccount, setAccountId, createAccount, accountPermissionUpdate, getRewardInfo, getBrokerageInfo.
  • Transfers / transactions: createTransaction, signAndBroadcast, sendTrx, createCommonTransaction, getTransactionById, getTransactionInfoById, getTransactionInfoByBlockNum, getTransactionSignWeight, getTransactionApprovedList, getTransactionFromPending, getTransactionListFromPending, getPendingSize.
  • Staking / resources: freezeBalanceV2, unfreezeBalanceV2, unfreezeBalance, withdrawExpireUnfreeze, cancelAllUnfreezeV2, delegateResource, unDelegateResource, withdrawBalance, updateBrokerage, plus the delegate / unfreeze queries (getDelegatedResource(V2), getDelegatedResourceAccountIndex(V2), getCanDelegatedMaxSize, getAvailableUnfreezeCount, getCanWithdrawUnfreezeAmount).
  • Smart contracts: deployContract, triggerContract, triggerConstantContract, estimateEnergy, getTrc20Balance, updateSetting, updateEnergyLimit, clearContractABI, getContract, getContractInfo.
  • Witnesses / voting: createWitness, updateWitness, voteWitnessAccount, listWitnesses, getPaginatedNowWitnessList.
  • TRC10 assets: createAssetIssue, transferAsset, participateAssetIssue, unfreezeAsset, updateAsset, and the asset queries (getAssetIssueByAccount, getAssetIssueByName, getAssetIssueListByName, getAssetIssueById, getAssetIssueList, getPaginatedAssetIssueList).
  • Governance / markets: proposals (proposalCreate/Approve/Delete, listProposals, getPaginatedProposalList, getProposalById), exchanges (exchangeCreate/Inject/Withdraw/Transaction, listExchanges, getPaginatedExchangeList, getExchangeById), storage (buyStorage, buyStorageBytes, sellStorage), and the on-chain market (marketSellAsset, marketCancelOrder, getMarketOrderById, getMarketOrderByAccount, getMarketPriceByPair, getMarketOrderListByPair, getMarketPairList).
  • Node / chain: getChainParameters, listNodes, getNodeInfo, totalTransaction, getNextMaintenanceTime, getBurnTrx, getBandwidthPrices, getEnergyPrices, getMemoFee.
  • Shielded (zk-SNARK): the full CreateShieldedTransaction… / ScanNote… / shielded-TRC20 family, exposed as readable passthroughs.

Utilities: address (isAddress, toBase58Address, toHexAddress, base58ToBytes, bytesToBase58Address, decodeAddress), units (trxToSun, sunToTrx, toBaseUnits, fromBaseUnits), crypto (privateKeyToAddress, fromMnemonic, signMessage, signTransactionId, signDigest), and ABI/hex helpers (encodeFunctionData, encodeAddressParam, encodeUint256, decodeUint256, functionSelector, stripHexPrefix, hexToBytesSafe, longToBytesBE).

Deprecated v1 duplicates that return a bare Transaction (e.g. CreateTransaction, legacy FreezeBalance) are intentionally not wrapped — their …2 / TransactionExtention forms are implemented instead. Use request<T>() if you need a raw v1 call.


Assumptions

  • Tronscan's public REST API is mainnet-only and keyless. Cross-check tests therefore run on mainnet against stable fixtures (a dormant treasury address, the canonical USDT contract, an immutable historical block + transaction). The TRON-PRO-API-KEY header is not sent to Tronscan (it 401s otherwise).
  • The committed test key targets Nile. Broadcast tests run there; the test skips broadcast gracefully if that account has no testnet TRX.
  • TRX has 6 decimals; TRC20 decimals default to 6 (USDT) and are a parameter.
  • Live-balance cross-checks read both sources near-simultaneously and retry until two reads agree, to avoid a false failure if a balance moves mid-test.
  • Node 20+ is assumed for global fetch.