Example: tron_sol_events
To run this example:
- Clone the examples repository:
git clone https://github.com/throgxyz/examples.git - Run:
cargo run -p examples-sol-macro --example tron_sol_events
//! Query and decode typed events with `tron_sol!`-generated filters.
//!
//! When an `#[sol(rpc)]` interface declares `event` items, `tron_sol!` generates
//! a per-event filter method on the `Instance`:
//!
//! * `token.Transfer_filter()` → a [`TronEventFilter`] typed to `Transfer`
//! * `.address(addr)` narrows it to one emitter
//! * `.query_tx(tx_id)` → decode matching events from one transaction
//! * `.query_block(num)` → decode matching events from a whole block
//!
//! TRON has no `eth_getLogs`, so events are read from transaction receipts. To
//! scan a range, call `query_block` in a loop (concurrently for throughput).
//!
//! No private key required (read-only).
//!
//! Required env (one of):
//! TRON_TX_ID — hex tx id that emitted Transfer events, OR
//! TRON_BLOCK — block number to scan
//!
//! Optional env:
//! TRON_CONTRACT — restrict to events from this contract address
//! TRON_API_KEY — TronGrid API key
//!
//! ```bash
//! TRON_TX_ID=<txid> cargo run -p examples-sol-macro --example tron_sol_events
//! TRON_BLOCK=<num> cargo run -p examples-sol-macro --example tron_sol_events
//! ```
use tronz::{
ProviderBuilder, TRONGRID_NILE,
contract::tron_sol,
primitives::{Address as TronAddress, B256},
};
tron_sol! {
#[sol(rpc)]
interface IErc20 {
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
function transfer(address to, uint256 amount) external returns (bool);
}
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let api_key = std::env::var("TRON_API_KEY").ok();
let contract = std::env::var("TRON_CONTRACT").ok();
let provider = ProviderBuilder::new().maybe_api_key(api_key).on_grpc(TRONGRID_NILE).await?;
// Any address works as the binding target; the filter can be scoped to a
// specific emitter separately via `.address(...)`.
let bind_addr: TronAddress = match &contract {
Some(c) => c.parse()?,
None => "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t".parse()?,
};
let token = IErc20::new(bind_addr, provider);
// Build a typed filter for the `Transfer` event, optionally narrowed to the
// configured contract address.
let mut filter = token.Transfer_filter();
if let Some(c) = &contract {
filter = filter.address(c.parse()?);
}
// ── Query by transaction id ───────────────────────────────────────────────
if let Ok(tx_hex) = std::env::var("TRON_TX_ID") {
let tx_id = B256::from_slice(&hex::decode(tx_hex.trim_start_matches("0x"))?);
let transfers = filter.query_tx(tx_id).await?;
println!("=== Transfer events in tx 0x{} ===", hex::encode(tx_id));
print_transfers(&transfers);
return Ok(());
}
// ── Query by block number ─────────────────────────────────────────────────
if let Ok(block_str) = std::env::var("TRON_BLOCK") {
let block_num: i64 = block_str.parse()?;
let transfers = filter.query_block(block_num).await?;
println!("=== Transfer events in block {block_num} ===");
print_transfers(&transfers);
return Ok(());
}
anyhow::bail!("set TRON_TX_ID or TRON_BLOCK");
}
fn print_transfers(transfers: &[IErc20::Transfer]) {
if transfers.is_empty() {
println!(" (none)");
return;
}
for (i, t) in transfers.iter().enumerate() {
// Indexed `from`/`to` are `alloy_primitives::Address`; convert to a
// tronz `Address` to display in base58check (`T...`).
let from: TronAddress = t.from.into();
let to: TronAddress = t.to.into();
println!(" [{i}] {from} -> {to} : {}", t.value);
}
}Find the source code on GitHub here.
