Skip to content

Repository files navigation

Tronz

An idiomatic, async-first Rust SDK for the TRON network — inspired by alloy.

Crates.io docs.rs License: MIT / Apache-2.0 Rust 1.91.1+ CI

Features

  • gRPC transport — connects to TronGrid, FullNodes, and SolidityNodes via tonic
  • Solidified state — read-only SolidityProvider queries irreversible blocks, accounts, transactions, and contract state
  • Resilient by default — per-call timeouts plus automatic retries with exponential back-off and jitter, configurable via ProviderBuilder / GrpcTransport::builder()
  • Failover — load-balance and fail over across multiple equivalent endpoints (with_endpoints, tonic balance_list)
  • Typed provider — fluent builder API for every native contract operation
  • Filler chain — automatic fee-limit and signing, plus optional explicit TAPOS overrides (mirrors alloy's JoinFill)
  • TRX / TRC10 / TRC20 — transfers, balance queries, and token metadata
  • Staking — Stake 2.0 (freeze, unfreeze, delegate, undelegate, claim rewards) and Stake 1.0 legacy (freeze_balance_v1, unfreeze_balance_v1)
  • HD wallets — BIP-39 mnemonic generation and BIP-44 key derivation (signer-mnemonic feature, TRON coin type 195)
  • Keystore — Web3 Secret Storage V3 encrypt/decrypt (signer-keystore feature, compatible with TronLink and gotron-sdk)
  • AWS KMS — sign with a key that never leaves the HSM (signer-aws feature, AwsSigner)
  • tron_sol! macro — type-safe contract bindings with typed call/event builders and JSON ABI file path support (superset of alloy's sol!)
  • TRC721Trc721Instance: transfer_from, approve, owner_of, token_uri, and standard ERC-721 queries
  • Contract deploy & call — native TronAbi metadata with an Alloy JsonAbi bridge, DeployBuilder, dynamic calls, and energy estimation
  • Event decoding — decode and filter logs with SolEvent
  • Votes & account management — SR voting, account activation, name and permission updates
  • Super representativesWitnessApi: become SR, update URL, update brokerage ratio
  • GovernanceGovernanceApi: list, query, submit, approve, and cancel chain-parameter proposals
  • TRC10 extended — participate in ICOs, release frozen supply, update token metadata, look up by name

Sponsors

tronz is grateful to the organizations that support its ongoing development and maintenance.

CatFee logo
CatFee.IO|TRON Service Provider
Project Sponsor

Installation

cargo add tronz

Or add it to your Cargo.toml manually:

[dependencies]
tronz = "0.5"

Optional features:

Feature Adds
full Everything below except signer-aws, which needs an AWS account
signer-mnemonic BIP-39 mnemonic generation + BIP-44 HD derivation (MnemonicBuilder)
signer-keystore Web3 Secret Storage V3 encrypt/decrypt (LocalSigner::encrypt_keystore, decrypt_keystore)
signer-tip712 TIP-712 typed-data signing (TronSigner::sign_typed_data) — TronWeb signTypedData-compatible
signer-aws AWS KMS signer (AwsSigner) — the private key never leaves the HSM
provider-grpc gRPC transport without TLS — use for local or private nodes

Quick start

Read the latest block

use tronz::{ProviderBuilder, TronProvider, TRONGRID_MAINNET};

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let provider = ProviderBuilder::new()
        .connect_grpc(TRONGRID_MAINNET)
        .await?;

    let block = provider.get_now_block().await?;
    println!("block #{} at {}ms", block.number, block.timestamp);
    Ok(())
}

Send TRX

use tronz::{LocalSigner, ProviderBuilder, TronProvider, Trx, TRONGRID_NILE};

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let signer = LocalSigner::from_hex("YOUR_PRIVATE_KEY")?;
    let to = "TRecipientAddress".parse()?;

    let provider = ProviderBuilder::new()
        .with_signer(signer)
        .connect_grpc(TRONGRID_NILE)
        .await?;

    let pending = provider
        .send_trx()
        .to(to)
        .amount(Trx::from_sun(1_000_000)?) // 1 TRX
        .send()
        .await?;

    let receipt = pending.get_receipt().await?;
    println!("confirmed in block #{}", receipt.block_number);
    Ok(())
}

Send from a multisig account

.build() stops at the unsigned transaction so several keys can sign it, and .permission_id(id) authorizes through an active permission instead of the account's owner permission.

use tronz::{
    LocalSigner, ProviderBuilder, TronNetworkWallet, TronProvider, TronWallet, Trx,
    TRONGRID_NILE,
};
use tronz::providers::types::SignedTransaction;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let a = LocalSigner::from_hex("FIRST_PRIVATE_KEY")?;
    let b = LocalSigner::from_hex("SECOND_PRIVATE_KEY")?;
    let account = "TMultisigAccount".parse()?;
    let to = "TRecipientAddress".parse()?;

    let mut wallet = TronWallet::new(a.clone());
    wallet.register_signer(b.clone());

    let provider = ProviderBuilder::new()
        .wallet(wallet.clone())
        .connect_grpc(TRONGRID_NILE)
        .await?;

    let raw = provider
        .send_trx()
        .from(account)
        .to(to)
        .amount(Trx::from_sun(1_000_000)?)
        .permission_id(2)
        .build()
        .await?;

    let keys = [a.address(), b.address()];
    let signatures = wallet.sign_hash_with_many(&keys, &raw.tx_id()).await?;
    let signed = SignedTransaction { raw, signatures };

    // Check the threshold before spending bandwidth on a rejected transaction.
    let weight = provider.get_transaction_sign_weight(&signed).await?;
    if weight.current_weight >= weight.required_weight {
        provider.broadcast(signed).await?;
    }
    Ok(())
}

Call a TRC20 contract

use tronz::{ProviderBuilder, TronProvider, TRONGRID_MAINNET};
use tronz::contract::Trc20Ext as _;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let provider = ProviderBuilder::new()
        .connect_grpc(TRONGRID_MAINNET)
        .await?;

    // USDT on mainnet
    let usdt = "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t".parse()?;
    let holder = "THoldersAddress".parse()?;

    let token = provider.trc20(usdt).caller(holder);
    let balance = token.balance_of(holder).await?;
    let decimals = token.decimals().await?;

    println!("balance: {} (decimals: {})", balance, decimals);
    Ok(())
}

Stake TRX and delegate energy

use tronz::{LocalSigner, ProviderBuilder, TronProvider, ResourceCode, TRONGRID_NILE, parse_trx};

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let signer = LocalSigner::from_hex("YOUR_PRIVATE_KEY")?;
    let receiver = "TReceiverAddress".parse()?;

    let provider = ProviderBuilder::new()
        .with_signer(signer)
        .connect_grpc(TRONGRID_NILE)
        .await?;

    // Freeze 100 TRX for energy
    provider
        .freeze_balance()
        .amount(parse_trx("100")?)
        .resource(ResourceCode::Energy)
        .send()
        .await?
        .get_receipt()
        .await?;

    // Delegate the energy to another account
    provider
        .delegate_resource()
        .resource(ResourceCode::Energy)
        .amount(parse_trx("100")?)
        .to(receiver)
        .send()
        .await?
        .get_receipt()
        .await?;

    Ok(())
}

Derive a signer from a mnemonic phrase

use tronz::{MnemonicBuilder, TronSigner, coins_bip39::English};

fn main() -> anyhow::Result<()> {
    let phrase = "abandon abandon abandon abandon abandon abandon \
                  abandon abandon abandon abandon abandon about";

    // Default path: m/44'/195'/0'/0/0 (TRON BIP-44 coin type 195)
    let signer = MnemonicBuilder::<English>::default()
        .phrase(phrase)
        .index(0)?
        .build()?;
    println!("address: {}", signer.address());

    // Generate a fresh random 24-word mnemonic
    let (signer, phrase) = MnemonicBuilder::<English>::default()
        .word_count(24)
        .build_random()?;
    println!("new phrase: {phrase}");
    println!("address:    {}", signer.address());
    Ok(())
}

Requires the signer-mnemonic feature.

Encrypt and decrypt a keystore

use tronz::{LocalSigner, TronSigner};

fn main() -> anyhow::Result<()> {
    let signer = LocalSigner::from_hex("YOUR_PRIVATE_KEY")?;

    // Encrypt to a JSON file (scrypt N=2^18, AES-128-CTR)
    let dir = std::path::Path::new("/tmp");
    let path = signer.encrypt_keystore(dir, "my-password")?;
    println!("saved: {}", path.display());

    // Decrypt back
    let recovered = LocalSigner::decrypt_keystore(&path, "my-password")?;
    assert_eq!(signer.address(), recovered.address());
    Ok(())
}

Requires the signer-keystore feature. The format is compatible with TronLink and gotron-sdk.

Query governance proposals

use tronz::{ProviderBuilder, TRONGRID_MAINNET};
use tronz::providers::ext::GovernanceApi as _;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let provider = ProviderBuilder::new()
        .connect_grpc(TRONGRID_MAINNET)
        .await?;

    let proposals = provider.list_proposals().await?;
    for p in &proposals {
        println!("proposal #{}: {:?}", p.proposal_id, p.state);
    }

    let p = provider.get_proposal_by_id(1).await?;
    println!("proposal #1 parameters: {:?}", p.parameters);
    Ok(())
}

List super representatives

use tronz::{ProviderBuilder, TronProvider, TRONGRID_MAINNET};

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let provider = ProviderBuilder::new()
        .connect_grpc(TRONGRID_MAINNET)
        .await?;

    let mut witnesses = provider.list_witnesses().await?;
    witnesses.sort_by_key(|w| std::cmp::Reverse(w.vote_count));
    for w in witnesses.iter().take(5) {
        println!("{}: {} votes", w.address, w.vote_count);
    }
    Ok(())
}

Query solidified (irreversible) state

SolidityProvider talks to a TRON SolidityNode (WalletSolidity), which only serves state confirmed by 2/3+ of the super representatives — i.e. irreversible. It is read-only by construction: no signer, no fillers, no broadcast. Use it when finality matters (exchange credits, settlement) and poll wait_for_success to block until a transaction has solidified and its execution succeeded.

use tronz::{SolidityProvider, TRONGRID_MAINNET_SOLIDITY};

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let solidity = SolidityProvider::connect(TRONGRID_MAINNET_SOLIDITY).await?;

    // Latest solidified head.
    let head = solidity.get_now_block().await?;
    println!("solidified head: {}", head.number);

    // Block until a broadcast transaction is solidified *and* succeeded.
    let tx_id = std::env::var("TRON_TX_ID")?.parse()?;
    let receipt = solidity.wait_for_success(tx_id).await?;
    println!("solidified in block {}", receipt.block_number);
    Ok(())
}

After broadcasting on a FullNode you can bridge straight to solidification via the pending handle — pass the SolidityProvider to poll:

let pending = provider.send_trx().to(to).amount(amount).send().await?;
// waits for irreversibility, and rejects a reverted transaction
let receipt = pending.require_success().get_solidified_receipt(&solidity).await?;

Crates

Crate Description
tronz SDK facade for the commonly used crates and APIs
tronz-primitives Address, Trx, ResourceCode, RecoverableSignature
tronz-abi Native TRON ABI metadata and optional Alloy JsonAbi conversion
tronz-rpc-types TRON's domain model and the protobuf messages behind it — no network stack
tronz-signer TronSigner, TronSignerSync, TronNetworkWallet, TronWallet, and LocalSigner
tronz-provider FullNode and SolidityNode transports/providers, fillers, extension traits
tronz-contract ContractInstance, DeployBuilder, TRC20 bindings, event decoding
tronz-sol-macro tron_sol! procedural macro
tronz-signer-aws AWS KMS signer (signer-aws feature)

Extension traits

Import these to unlock additional methods on any provider:

Trait Import Methods
Trc10Api use tronz::providers::ext::Trc10Api as _ issue, transfer, balance, participate, update, look up by name
WitnessApi use tronz::providers::ext::WitnessApi as _ list SRs, brokerage, become SR, update URL/brokerage
GovernanceApi use tronz::providers::ext::GovernanceApi as _ list/fetch proposals, submit, approve, cancel
ExchangeApi use tronz::providers::ext::ExchangeApi as _ create exchange, inject/withdraw liquidity, trade
MarketApi use tronz::providers::ext::MarketApi as _ market orders, sell/cancel, query prices

Examples

42 runnable examples are in throgxyz/examples, organized by category. All target the Nile testnet.

git clone https://github.com/throgxyz/examples
cd examples

# Read-only queries (no key needed)
cargo run -p examples-queries --example query
cargo run -p examples-queries --example list_witnesses
cargo run -p examples-queries --example governance_list

# Send TRX on Nile testnet
TRON_PRIVATE_KEY=<hex> cargo run -p examples-transfers --example transfer_trx

# TRC20 balance + transfer
TRON_PRIVATE_KEY=<hex> cargo run -p examples-trc20 --example trc20

# Stake 2.0: freeze + delegate + claim rewards
TRON_PRIVATE_KEY=<hex> cargo run -p examples-staking --example stake

# TRC10: issue a new token
TRON_PRIVATE_KEY=<hex> cargo run -p examples-trc10 --example trc10_issue

# Deploy and call a smart contract
TRON_PRIVATE_KEY=<hex> cargo run -p examples-contracts --example contract_deploy

# HD wallet: derive from mnemonic
cargo run -p examples-signers --example signer_mnemonic

Endpoints

Network Constant Endpoint
Mainnet (TLS) TRONGRID_MAINNET https://grpc.trongrid.io:443
Mainnet SolidityNode TRONGRID_MAINNET_SOLIDITY http://grpc.trongrid.io:50052
Nile testnet TRONGRID_NILE http://grpc.nile.trongrid.io:50051
Nile SolidityNode TRONGRID_NILE_SOLIDITY http://grpc.nile.trongrid.io:50061
use tronz::{
    TRONGRID_MAINNET, TRONGRID_MAINNET_SOLIDITY, TRONGRID_NILE,
    TRONGRID_NILE_SOLIDITY,
};

Minimum Supported Rust Version

1.91.1 (Rust 2024 edition).

License

Licensed under either of Apache License, Version 2.0 or MIT license at your option.

About

Idiomatic, async-first Rust SDK for the TRON network — inspired by alloy

Topics

Resources

Contributing

Security policy

Stars

54 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages