Skip to main content

Tron Proof Validation Guide

Overview

Cross-chain proof validation between TRON and EVM chains is fully compatible with proper address format handling. With proper format conversion, the same proof can be validated successfully on both TRON and EVM chains, providing true cross-chain interoperability.

The key principles are:

🔒 Data Integrity

All proof data remains identical across chains

📍 Address Formats

TRON uses base58, EVM uses hex format

🔗 Cross-Chain

Same proof validates on both networks

Network Information

NetworkChain IDPublic RPCExplorerProver Contract
TRON Nile Testnet3448148188nile.trongrid.ionile.tronscan.orgTN1hKC3qzkRzRgSCzmEY613KLjMJHqQ1Ly
TRON Mainnet728126428api.trongrid.io/jsonrpctronscan.orgTNKCTuonyurjAXBYpZtSZEo7WdFUKW9cbN

Address Format Differences

Understanding address formats is crucial for TRON integration:

Address Format Comparison

Format TypeExampleUsage
TRON Base58TXYZopYRdj2D9XRtbG411XZZ3kM5VkAeBfTRON Explorer, TronWeb interactions
TRON Hex41eca9bc828a3005b9a3b909f2cc5c2a54794de05fRaw logs, internal processing
EVM Hex0xECa9bC828A3005B9a3b909f2cc5c2a54794DE05FStandard EVM format
info

Key Insight: These are the same address in different formats. TRON uses 41 prefix instead of 0x for hex addresses.

Cross-Chain Proof Validation

EVM to TRON Validation

When validating proofs from EVM chains (like Optimism) on TRON, the data remains consistent with only address format differences:

EVM to TRON Proof Validation Example
// Same proof validated on different chains

// --- Base Sepolia Validation Result ---
const baseResult = {
chainId: 11155420,
emittingContract: "0xB84644c24B4D0823A0770ED698f7C20B88Bcf824", // EVM format
topics: "0xc0ae438737d82fdd04b48b08fb95c82fdbc3e4c6afb...",
unindexedData: "0x00000000000000000000000000000000000000000000000029a2241af62c0000..."
};

// --- TRON Nile Validation Result ---
const tronResult = {
chainId: 11155420n, // Same chain ID
emittingContract: "41b84644c24b4d0823a0770ed698f7c20b88bcf824", // TRON format
topics: "0xc0ae438737d82fdd04b48b08fb95c82fdbc3e4c6afb...", // Identical
unindexedData: "0x00000000000000000000000000000000000000000000000029a2241af62c0000..." // Identical
};

// Comparison Results
console.log("Chain ID Match:", baseResult.chainId === Number(tronResult.chainId)); // ✅ true
console.log("Topics Match:", baseResult.topics === tronResult.topics); // ✅ true
console.log("Data Match:", baseResult.unindexedData === tronResult.unindexedData); // ✅ true

// Only difference: address format
const convertedAddress = "0x" + tronResult.emittingContract.substring(2);
console.log("Address Match:", baseResult.emittingContract.toLowerCase() === convertedAddress.toLowerCase()); // ✅ true

TRON to EVM Validation

When validating TRON events on EVM chains, the prover automatically handles format conversion:

Original TRON Transaction: View on Explorer

TRON Raw Event Log
{
"address": "eca9bc828a3005b9a3b909f2cc5c2a54794de05f", // No prefix
"topics": [
"ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", // Event signature
"0000000000000000000000003701e24fabdec5be006d04331b977d974a0e64c8", // From address
"000000000000000000000000d3ba0080540dd44a65e299d215c594a89c820fc5" // To address
],
"data": "0000000000000000000000000000000000000000000000000000000000989680" // Amount
}

TRON Contract Interactions

Static Calls (Read-Only)

Method 1: contract.at() - Recommended
const tronWeb = new TronWeb({
fullHost: "https://nile.trongrid.io"
});

// Using base58 address format
const contract = await tronWeb.contract().at("TD39R92XN4HqXUQCPmtnZV1mhcgMy7Qbn8");

try {
const result = await contract.validateEvent(proofHex).call();

console.log("Validation Result:", {
chainId: result.chainId.toString(),
emittingContract: result.emittingContract, // TRON hex format (41...)
topics: result.topics,
unindexedData: result.unindexedData
});
} catch (error) {
console.error("Validation failed:", error.message);
}

Advantages: Simple syntax, automatic ABI handling, and built-in error handling

Transactions (State-Changing)

Method 1: contract.method().send()
const tronWeb = new TronWeb({
fullHost: "https://nile.trongrid.io",
privateKey: "your_private_key_here" // Required for transactions
});

const contract = await tronWeb.contract().at("TContractAddress123");

try {
const tx = await contract.setValueFromSource(proofHex).send({
feeLimit: 100_000_000, // 100 TRX max fee (in SUN)
callValue: 0, // TRX to send with transaction
shouldPollResponse: false // Don't wait for confirmation
});

console.log("Transaction ID:", tx);

// Optional: Wait for confirmation
const receipt = await tronWeb.trx.getTransactionInfo(tx);
console.log("Transaction receipt:", receipt);

} catch (error) {
console.error("Transaction failed:", error);
}

Resource Management

TRON uses Energy & Bandwidth instead of gas:

TRON Resource System

ResourcePurposeCost
EnergySmart contract executionConsumed per operation
BandwidthTransaction sizeConsumed per byte
TRXBackup paymentWhen resources insufficient
Check Account Resources
const account = await tronWeb.trx.getAccountResources(address);

console.log("Available Resources:", {
energy: account.EnergyLimit - account.EnergyUsed,
bandwidth: account.freeNetLimit - account.freeNetUsed,
frozenBalance: account.frozen?.[0]?.frozen_balance || 0
});

// Estimate resource usage
const energyEstimate = await tronWeb.transactionBuilder.estimateEnergy(
contractAddress,
functionSelector,
options,
parameters,
issuerAddress
);

Type Definitions & Common Patterns

Parameter Type Mapping

TRON Parameter Types
// Common type conversions for TRON
const parameterExamples = [
// Addresses
{type: 'address', value: "TAddress123..."}, // TRON base58 address
{type: 'address', value: "41eca9bc828a3005b..."}, // TRON hex address

// Numbers (always as strings for large numbers)
{type: 'uint256', value: "1000000000000000000"}, // 1 ETH in wei
{type: 'uint32', value: "11155420"}, // Chain ID
{type: 'uint8', value: "18"}, // Decimals

// Bytes data
{type: 'bytes', value: "0x1234abcd"}, // Hex data
{type: 'bytes32', value: "0x" + "0".repeat(64)}, // 32-byte hash

// Strings and booleans
{type: 'string', value: "Hello TRON"}, // Text data
{type: 'bool', value: true} // Boolean
];

// Address conversion utilities
function convertAddressFormats(address) {
if (address.startsWith('T')) {
// Base58 to hex
return TronWeb.address.toHex(address);
} else if (address.startsWith('41')) {
// TRON hex to EVM hex
return '0x' + address.substring(2);
} else if (address.startsWith('0x')) {
// EVM hex to TRON hex
return '41' + address.substring(2);
}
throw new Error('Invalid address format');
}

Error Handling & Best Practices

Common Issues & Solutions

warning

Common Pitfalls: Address format mismatches, insufficient resources, and incorrect parameter types are the most frequent issues.

Robust Error Handling
async function validateProofOnTron(proofHex) {
try {
// 1. Check account resources first
const resources = await tronWeb.trx.getAccountResources(
tronWeb.defaultAddress.base58
);

if (resources.EnergyLimit - resources.EnergyUsed < 15000) {
console.warn("Low energy. Consider freezing TRX for energy.");
}

// 2. Validate proof format
if (!proofHex.startsWith('0x')) {
proofHex = '0x' + proofHex;
}

// 3. Call contract with proper error handling
const contract = await tronWeb.contract().at(PROVER_CONTRACT_ADDRESS);
const result = await contract.validateEvent(proofHex).call();

// 4. Process result with address format conversion
return {
chainId: result.chainId.toString(),
emittingContract: convertToEvmFormat(result.emittingContract),
topics: result.topics,
unindexedData: result.unindexedData
};

} catch (error) {
// Handle specific TRON errors
if (error.message.includes('REVERT')) {
console.error("Contract reverted:", error.message);
} else if (error.message.includes('OUT_OF_ENERGY')) {
console.error("Insufficient energy for transaction");
} else if (error.message.includes('BANDWIDTH_NOT_ENOUGH')) {
console.error("Insufficient bandwidth");
} else {
console.error("Unknown error:", error);
}
throw error;
}
}

function convertToEvmFormat(tronAddress) {
if (tronAddress.startsWith('41')) {
return '0x' + tronAddress.substring(2);
}
return tronAddress; // Already in correct format
}

Complete Integration Example

Full TRON Proof Validation Flow
import TronWeb from 'tronweb';

class TronProofValidator {
constructor(nodeUrl, privateKey = null) {
this.tronWeb = new TronWeb({
fullHost: nodeUrl,
privateKey: privateKey
});
}

async validateCrossChainProof(proofHex, contractAddress) {
try {
// Initialize contract
const contract = await this.tronWeb.contract().at(contractAddress);

// Validate proof
const result = await contract.validateEvent(proofHex).call();

// Process and return standardized result
return this.processValidationResult(result);

} catch (error) {
throw new Error(`TRON validation failed: ${error.message}`);
}
}

processValidationResult(result) {
return {
chainId: parseInt(result.chainId.toString()),
emittingContract: this.convertToEvmAddress(result.emittingContract),
topics: this.parseTopics(result.topics),
unindexedData: result.unindexedData,
// Keep original TRON format for reference
originalTronContract: result.emittingContract
};
}

convertToEvmAddress(tronAddress) {
if (tronAddress.startsWith('41')) {
return '0x' + tronAddress.substring(2);
}
return tronAddress;
}

parseTopics(topicsHex) {
// Split concatenated topics into array
if (!topicsHex.startsWith('0x')) return [];

const cleanHex = topicsHex.substring(2);
const topics = [];

for (let i = 0; i < cleanHex.length; i += 64) {
topics.push('0x' + cleanHex.substring(i, i + 64));
}

return topics;
}
}

// Usage example
const validator = new TronProofValidator("https://nile.trongrid.io");
const result = await validator.validateCrossChainProof(
proofHex,
"TD39R92XN4HqXUQCPmtnZV1mhcgMy7Qbn8"
);

console.log("Validation successful:", result);
tip

Best Practice: Always convert addresses to a consistent format in your application logic to avoid comparison issues between TRON and EVM chains.