Example: contract_call
To run this example:
- Clone the examples repository:
git clone https://github.com/throgxyz/examples.git - Run:
cargo run -p examples-contracts --example contract_call
//! Constant contract call — read state without spending energy.
//!
//! `trigger_constant_contract` simulates execution and returns output without
//! creating a transaction. It's the TRON equivalent of `eth_call`.
//!
//! This example calls a TRC20 contract three ways:
//!
//! 1. **`Trc20Instance`** — high-level typed API, no ABI file
//! 2. **Raw calldata** — manually ABI-encode selector and decode output
//! 3. **Dynamic `Interface`** — load a JSON ABI and call by function name
//!
//! No private key required (read-only).
//!
//! Required env:
//! TRON_CONTRACT — TRC20 contract address (e.g. USDT on mainnet)
//!
//! Optional env:
//! TRON_API_KEY — TronGrid API key
//!
//! ```bash
//! TRON_CONTRACT=TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t \
//! cargo run -p examples-contracts --example contract_call
//! ```
use alloy_dyn_abi::DynSolValue;
use alloy_json_abi::JsonAbi;
use tronz::{
ProviderBuilder, TRONGRID_NILE,
contract::{ContractExt, Interface, SolCall, Trc20Ext, trc20::ITRC20},
};
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let contract_str = std::env::var("TRON_CONTRACT").expect("TRON_CONTRACT env var required");
let api_key = std::env::var("TRON_API_KEY").ok();
let contract: tronz::Address = contract_str.parse()?;
// ── Approach 1: Trc20Instance (typed, no ABI file) ───────────────────────
let provider =
ProviderBuilder::new().maybe_api_key(api_key.clone()).on_grpc(TRONGRID_NILE).await?;
let token = provider.trc20(contract);
println!("=== Trc20Instance (typed calls) ===");
println!(" name : {}", token.name().await?);
println!(" symbol : {}", token.symbol().await?);
println!(" decimals : {}", token.decimals().await?);
println!(" supply : {}", token.total_supply().await?);
// ── Approach 2: ContractInstance with raw calldata ────────────────────────
//
// Manually encode the `name()` selector and call `.call().await`.
let name_calldata: tronz::primitives::Bytes = ITRC20::nameCall {}.abi_encode().into();
let iface = Interface::empty();
let instance = provider.contract(contract, iface);
let output = instance.call_raw(name_calldata).call().await?;
let name = ITRC20::nameCall::abi_decode_returns(&output)?;
println!("\n=== Raw calldata + manual decode ===");
println!(" name (decoded) : {name}");
// ── Approach 3: Interface + JSON ABI (dynamic) ────────────────────────────
//
// Build an `Interface` from a JSON ABI string and call by function name.
// Useful when you don't have compile-time `sol!` bindings.
let abi_json = r#"[
{"name":"decimals","type":"function","inputs":[],"outputs":[{"type":"uint8"}],"stateMutability":"view"},
{"name":"totalSupply","type":"function","inputs":[],"outputs":[{"type":"uint256"}],"stateMutability":"view"}
]"#;
let abi: JsonAbi = serde_json::from_str(abi_json)?;
let interface = Interface::new(abi);
let dyn_instance = provider.contract(contract, interface);
println!("\n=== Dynamic ABI call ===");
let decimals_vals = dyn_instance.call("decimals", &[]).await?;
println!(" decimals : {:?}", decimals_vals.first());
let supply_vals = dyn_instance.call("totalSupply", &[]).await?;
if let Some(DynSolValue::Uint(supply, _)) = supply_vals.first() {
println!(" totalSupply: {supply}");
}
Ok(())
}Find the source code on GitHub here.
