Interacting with contracts

Read state and call methods on a deployed TRON smart contract — both read-only queries (no Energy, no signature) and state-changing transactions (consume Energy, require signature).

📘

Prerequisites

Once a contract is deployed, you can read its state and call its methods. TRON splits contract interactions into two categories based on what the call does:

  • Read methods (view / pure functions) — query state without changing it. No signature required, no Energy consumed.
  • Write methods (state-changing functions) — modify contract state. Require a signature and consume Energy (and a small amount of Bandwidth).

This page covers both, plus how to inspect a deployed contract's ABI and bytecode.

Read methods (no Energy, no signature)

Read methods run locally on a node — no transaction is broadcast, no on-chain state changes. The TVM still executes the function, but the result is returned to the caller directly rather than being recorded on-chain. Use this for:

  • Reading a token balance (balanceOf)
  • Checking ownership (ownerOf, isApprovedForAll)
  • Pre-computing values to avoid sending an unnecessary transaction

TronWeb (.call())

const contract = await tronWeb.contract().at(contractAddress);
const balance = await contract.balanceOf('TM2TmqauSEiRf16CyFgzHV2BVxBejY9iyR').call();
console.log('Balance:', balance.toString());

The .call() method runs the function locally and returns the value. Use it for any function declared view or pure in Solidity.

HTTP API — wallet/triggerconstantcontract

The Fullnode endpoint wallet/triggerconstantcontract runs a function locally without broadcasting:

BASE_URL=https://api.shasta.trongrid.io   # example — replace with any TRON node (TronGrid, third-party, or self-hosted)
curl -X POST ${BASE_URL}/wallet/triggerconstantcontract -d '{
  "contract_address": "TG3XXyExBkPp9nzdajDZsozEu4BkaSJozs",
  "function_selector": "balanceOf(address)",
  "parameter": "000000000000000000000000977C20977F412C2A1AA4EF3D49FEE5EC4C31CDFB",
  "owner_address": "TPnBjYQEMo4Yd4866KCzXdi4a169KGd63n",
  "visible": true
}'

The response includes constant_result, the raw hex-encoded return value. To decode it (for example, a uint256 balance), see Parameter encoding and decoding.

Generate the address parameter dynamically

Do not reuse the encoded value from the example for another account. For balanceOf(address) and other methods that use the standard Solidity address type, TronWeb can convert a Base58Check address to hex. Remove the TRON 41 network prefix, then left-pad the 20-byte address to a 32-byte ABI word:

import { TronWeb } from 'tronweb';

const address = process.env.ACCOUNT_ADDRESS;
if (!TronWeb.isAddress(address)) {
  throw new Error('Set ACCOUNT_ADDRESS to a valid TRON address');
}

const parameter = TronWeb.address.toHex(address).slice(2).padStart(64, '0');
console.log(parameter);

Use the output as the request body's parameter value. For arrays, dynamic types, or non-standard ABIs, use the ABI-encoding methods described in Parameter encoding and decoding instead of assembling the value manually.

Write methods (consume Energy, require signature)

Write methods modify contract state and produce a transaction that must be signed and broadcast. Examples:

  • transfer — move TRC-20 tokens
  • mint / burn — change supply
  • setMessage — update a state variable

TronWeb (.send())

const contract = await tronWeb.contract().at(contractAddress);
const [txID, success] = await contract.transfer(
    'TWbcHNCYzqAGbrQteKnseKJdxfzBHyTfuh',  // recipient
    1_000_000                              // amount
).send({
    feeLimit: 100_000_000,    // 100 TRX, in sun
    callValue: 0,             // TRX to attach to the call
    shouldPollResponse: true, // wait for transaction confirmation
    keepTxID: true            // return both the txID and contract result
});
console.log('txID:', txID, 'success:', success);

TronWeb .send() builds, signs, and broadcasts the transaction. Without shouldPollResponse, it returns the txID immediately after a successful broadcast. With shouldPollResponse: true, it continues polling for the receipt and returns the decoded contract result. The example also sets keepTxID: true, so the return value is [txID, result]; for a standard TRC-20 transfer, result is normally a boolean.

HTTP API — wallet/triggersmartcontract

The Fullnode endpoint wallet/triggersmartcontract returns an unsigned transaction. You then sign and broadcast it:

BASE_URL=https://api.shasta.trongrid.io   # example — replace with any TRON node (TronGrid, third-party, or self-hosted)
curl -X POST ${BASE_URL}/wallet/triggersmartcontract -d '{
  "contract_address": "TG3XXyExBkPp9nzdajDZsozEu4BkaSJozs",
  "function_selector": "transfer(address,uint256)",
  "parameter": "00000000000000000000000015208EF33A926919ED270E2FA61367B2DA3753DA0000000000000000000000000000000000000000000000000000000000000032",
  "fee_limit": 100000000,
  "call_value": 0,
  "owner_address": "TPnBjYQEMo4Yd4866KCzXdi4a169KGd63n",
  "visible": true
}'

For the full sign-and-broadcast flow, see Smart contract deployment and invocation.

Read vs write at a glance

PropertyRead methods (view / pure)Write methods (state-changing)
TronWeb method.call().send()
HTTP endpointwallet/triggerconstantcontractwallet/triggersmartcontract
Energy costNone (executes locally)Consumes Energy
Bandwidth costNoneYes — for the broadcast transaction
Signature requiredNoYes
fee_limit requiredNoYes
Transaction recorded on-chainNoYes
📘

Pre-call before send

A common pattern is to .call() a function first to predict its return value, then .send() the same function if the prediction is correct. This avoids paying Energy for transactions that would have failed anyway.

Querying a contract's ABI and bytecode

To call methods on a contract whose ABI you do not have, fetch the contract's metadata directly from the chain.

wallet/getcontract — static information

Returns a SmartContract object with the ABI, deployment bytecode, name, and configuration parameters:

BASE_URL=https://api.shasta.trongrid.io   # example — replace with any TRON node (TronGrid, third-party, or self-hosted)
curl -X POST ${BASE_URL}/wallet/getcontract -d '{
  "value": "TG3XXyExBkPp9nzdajDZsozEu4BkaSJozs",
  "visible": true
}'

This is the simpler endpoint — use it when you only need the ABI to set up TronWeb's contract object.

wallet/getcontractinfo — full information

Returns everything getcontract does, plus runtime bytecode and dynamic Energy data (energy_usage, energy_factor):

BASE_URL=https://api.shasta.trongrid.io   # example — replace with any TRON node (TronGrid, third-party, or self-hosted)
curl -X POST ${BASE_URL}/wallet/getcontractinfo -d '{
  "value": "TG3XXyExBkPp9nzdajDZsozEu4BkaSJozs",
  "visible": true
}'

Use getcontractinfo when you need the runtime bytecode (for example, to verify the deployed code matches a known hash) or to inspect energy_factor for cost prediction. For most read-only and write-only interactions, getcontract is enough.

📘

ABI may be missing for factory-deployed contracts

Contracts created with the CREATE or CREATE2 opcode (rather than via wallet/deploycontract) do not store an ABI on-chain. Both getcontract and getcontractinfo may return an empty abi field for such contracts. In that case you'll need the ABI from the factory contract source or a third-party indexer.


Related resources