decode_log
Runnable example from the contracts package.
Run
git clone https://github.com/throgxyz/examples.git
cd examples
cargo run -p examples-contracts --example decode_logSource
//! Decode raw event logs from a receipt into typed `SolEvent` structs.
//!
//! No private key required (read-only).
//!
//! Required env:
//! TRON_TX_ID — hex transaction id that emitted TRC20 Transfer events
//! TRON_CONTRACT — TRC20 contract address (for the dynamic ABI approach)
//!
//! Optional env:
//! TRON_API_KEY — TronGrid API key
//!
//! ```bash
//! TRON_TX_ID=<txid> TRON_CONTRACT=<contract> \
//! cargo run -p examples-contracts --example decode_log
//! ```
use alloy_json_abi::JsonAbi;
use tronz::{
ProviderBuilder, TRONGRID_NILE, TronProvider,
contract::{ContractExt, Interface, SolEvent, decode_logs, trc20::ITRC20},
primitives::B256,
};
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let tx_id_hex = std::env::var("TRON_TX_ID").expect("TRON_TX_ID env var required");
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 tx_id_bytes = hex::decode(tx_id_hex.trim_start_matches("0x"))?;
let tx_id: B256 = B256::from_slice(&tx_id_bytes);
let contract: tronz::Address = contract_str.parse()?;
let provider =
ProviderBuilder::new().maybe_api_key(api_key).connect_grpc(TRONGRID_NILE).await?;
let info = provider
.get_transaction_info(tx_id)
.await?
.ok_or_else(|| anyhow::anyhow!("transaction not found or not yet confirmed"))?;
println!("=== Transaction 0x{} ===", hex::encode(tx_id));
println!(" status : {:?}", info.status);
println!(" logs : {}", info.logs.len());
println!("\n=== Static decode (ITRC20::Transfer) ===");
println!(" Transfer topic0 : 0x{}", hex::encode(ITRC20::Transfer::SIGNATURE_HASH));
let transfers: Vec<_> =
decode_logs::<ITRC20::Transfer>(&info.logs).collect::<Result<_, _>>()?;
if transfers.is_empty() {
println!(" no Transfer events found");
} else {
for (i, t) in transfers.iter().enumerate() {
let from: tronz::Address = t.from.into();
let to: tronz::Address = t.to.into();
println!(" [{}] from={from} to={to} value={}", i, t.value);
}
}
let approvals: Vec<_> =
decode_logs::<ITRC20::Approval>(&info.logs).collect::<Result<_, _>>()?;
if !approvals.is_empty() {
println!("\n Approval events:");
for (i, a) in approvals.iter().enumerate() {
let owner: tronz::Address = a.owner.into();
let spender: tronz::Address = a.spender.into();
println!(" [{}] owner={owner} spender={spender} value={}", i, a.value);
}
}
println!("\n=== Dynamic decode (Interface from JSON ABI) ===");
let abi_json = r#"[
{"name":"Transfer","type":"event","inputs":[{"name":"from","type":"address","indexed":true},{"name":"to","type":"address","indexed":true},{"name":"value","type":"uint256","indexed":false}],"anonymous":false},
{"name":"Approval","type":"event","inputs":[{"name":"owner","type":"address","indexed":true},{"name":"spender","type":"address","indexed":true},{"name":"value","type":"uint256","indexed":false}],"anonymous":false}
]"#;
let abi: JsonAbi = serde_json::from_str(abi_json)?;
let interface = Interface::new(abi);
let instance = provider.contract(contract, interface);
for (i, result) in instance.decode_logs(&info.logs).enumerate() {
match result {
Ok((name, decoded)) => {
println!(" [{}] event={}", i, name);
for val in &decoded.indexed {
println!(" indexed : {val:?}");
}
for val in &decoded.body {
println!(" body : {val:?}");
}
}
Err(e) => {
println!(" [{}] decode error: {e}", i);
}
}
}
Ok(())
}