/wallet/triggerconstantcontract¶
Read-only contract call (does not go on-chain). Used to read view/pure functions or simulate transactions.
- Source:
framework/src/main/java/org/tron/core/services/http/TriggerConstantContractServlet.java - Method:
POST - Contract:
protocol.TriggerSmartContract - Response:
api.TransactionExtention - Solidity endpoint:
/walletsolidity/triggerconstantcontract
Request parameters¶
| Field | Type | Required | Description |
|---|---|---|---|
owner_address |
string | Yes | Caller address (msg.sender in the contract) |
contract_address |
string | Conditional | Target contract address; contract_address or data must be provided |
function_selector |
string | No | Function signature |
parameter |
string | No | ABI-encoded parameters (hex) |
data |
string | No | Call data (hex); use either this or function_selector |
call_value |
int64 | No | TRX (sun) sent with the call |
token_id |
int64 | No | TRC-10 token id sent with the call |
call_token_value |
int64 | No | TRC-10 amount sent with the call |
extra_data |
string | No | Transaction memo (hex; UTF-8 text when visible=true) |
Permission_id |
int32 | No | Multi-sig permission ID |
visible |
bool | No | Format for addresses and text fields (response includes result.message, which is affected by visible) |
The required-field constraint is owner_address AND (contract_address OR data). Omitting contract_address is valid when data is supplied, for example when simulating contract deployment.
Example:
curl --request POST \
--url https://nile.trongrid.io/wallet/triggerconstantcontract \
--header 'accept: application/json' \
--header 'content-type: application/json' \
--data '
{
"owner_address": "41dd791d6b49e190062d650e6a23c575510d35f2f9",
"contract_address": "41eca9bc828a3005b9a3b909f2cc5c2a54794de05f",
"function_selector": "balanceOf(address)",
"parameter": "000000000000000000000000dd791d6b49e190062d650e6a23c575510d35f2f9"
}
'
Response¶
TransactionExtention:
| Field | Type | Description |
|---|---|---|
transaction |
Transaction | Unsigned transaction (context only — should not be signed and broadcast) |
txid |
string(hex) | Transaction hash |
constant_result |
repeated bytes(hex) | ABI-encoded hex of the function return value; on revert, this is the ABI-encoded revert reason (the leading 4 bytes 08c379a0 are the Error(string) selector) |
result |
Return | Status; on revert / failed require, result.result=false and result.message contains REVERT opcode executed or runtime error |
energy_used |
int64 | Estimated energy consumed |
energy_penalty |
int64 | Energy penalty (if any) |
logs |
repeated TransactionLog | Event logs (if emitted) |
internal_transactions |
repeated InternalTransaction | Internal calls (if any) |
Understanding simulation results
The execution context used for this simulation includes the state view available to the node when it processes the request and the TVM rules in effect at that time. Because the node continuously processes blocks and pending transactions and may run other constant calls concurrently, a transaction submitted later may execute on-chain in a different execution context, and its result may differ from the result of this simulation. This is an expected characteristic of point-in-time simulation.
The execution context used for a simulation is primarily affected by the following:
- Processing blocks: A node executes the transactions in a block sequentially, updating accounts, contract storage, resources, and chain parameters as it proceeds. A simulation initiated during this process uses the data visible to the node at that moment.
- Processing pending transactions:
/walletprovides a view of the latest state. While a node validates or replays pending transactions, this view may include local state updates and may continue to change as transactions are included in blocks or new blocks arrive. - Other constant calls: Calls made through
/walletand/walletsoliditymay use different proposal activation states and TVM rules. When calls with different execution contexts run concurrently, in rare cases a simulation may reflect the proposal activation state and TVM rules being applied by the node at that moment.
constant_result, energy_used, logs, and internal_transactions are produced by the current simulation. If the simulation reads different state or applies different TVM rules, the contract may follow a different execution path, and the values returned in these fields may also differ. The top-level result reports the status of the API request, while transaction.ret[0].ret reports the TVM execution result of the simulation.
Choose the endpoint based on the state view you need: /wallet provides the latest state, whereas /walletsolidity provides a slightly older, solidified state. When preparing a transaction, treat the simulation results as a reference and leave an appropriate margin in fee_limit to account for changes in Energy consumption as state changes. After broadcasting the transaction, rely on the transaction receipt for the actual on-chain execution result.
Response example (real Nile capture):
{
"result": { "result": true },
"energy_used": 935,
"constant_result": ["0000000000000000000000000000000000000000000000000000000040cfcc00"],
"transaction": {
"ret": [{}],
"visible": false,
"txID": "cff6488e738ce77f7325572fe0aa2470f87dbf1c95eeeb2c25feec59d5afa35c",
"raw_data": {
"contract": [
{
"parameter": {
"value": {
"data": "70a08231000000000000000000000000dd791d6b49e190062d650e6a23c575510d35f2f9",
"owner_address": "41dd791d6b49e190062d650e6a23c575510d35f2f9",
"contract_address": "41eca9bc828a3005b9a3b909f2cc5c2a54794de05f"
},
"type_url": "type.googleapis.com/protocol.TriggerSmartContract"
},
"type": "TriggerSmartContract"
}
],
"ref_block_bytes": "28c0",
"ref_block_hash": "9eabbe133123b34c",
"expiration": 1777447218000,
"timestamp": 1777447160779
},
"raw_data_hex": "0a0228c022089eabbe133123b34c40d0d6f0c0dd335a8e01081f1289010a31747970652e676f6f676c65617069732e636f6d2f70726f746f636f6c2e54726967676572536d617274436f6e747261637412540a1541dd791d6b49e190062d650e6a23c575510d35f2f9121541eca9bc828a3005b9a3b909f2cc5c2a54794de05f222470a08231000000000000000000000000dd791d6b49e190062d650e6a23c575510d35f2f970cb97edc0dd33"
}
}
txID/ref_block_*/expiration/timestamp/raw_data_hexand other ephemeral fields share semantics with/wallet/createtransaction. Constant calls do not go on-chain; thetransactionfield is provided only as context.Execution timeout: This endpoint uses the node's constant-call execution deadline.
vm.constantCallTimeoutMs = 0uses the network'sMAX_CPU_TIME_OF_ONE_TXlimit; a positive value sets a constant-call-only deadline in milliseconds. See TVM and constant-call configuration.
Error responses¶
This endpoint never writes {"Error": ...} after the request reaches the servlet. Servlet-handled exceptions are caught and written into result.code / result.message; the HTTP body is still a TransactionExtention. Note: EVM revert / runtime errors do not go through result.code — instead result.result=true, message carries the revert/runtime info, and the failure is marked at transaction.ret[0].ret="FAILED".
Before the request reaches this servlet, shared layers can still return a different shape: SizeLimitHandler usually returns HTTP 413 Payload Too Large for an oversized body, and a non-blocking rate-limit rejection returns HTTP 200 with {"Error":"class java.lang.IllegalAccessException : lack of computing resources"}.
| Trigger | result.result |
result.code |
result.message |
Other |
|---|---|---|---|---|
contract_address is set but the contract does not exist |
default (false) | CONTRACT_VALIDATE_ERROR |
Smart contract is not exist. |
— |
| The node has constant-call support disabled | default (false) | CONTRACT_VALIDATE_ERROR |
this node does not support constant |
— |
EVM revert / failed require |
true | default (SUCCESS, omitted) |
REVERT opcode executed |
transaction.ret[0].ret="FAILED"; constant_result[0] is the Error(string) ABI encoding (when the contract supplies a reason) |
EVM runtime error (OOG, illegal opcode, etc., no result.getException()) |
true | default (SUCCESS, omitted) |
Raw result.getRuntimeError() string |
Same as above |
result.getException() != null (e.g. OutOfTimeException) |
default (false) | OTHER_ERROR |
<exceptionClass> : <message> (" → ') |
— |
| Other (hex parsing, missing parameters, proto merge, etc.) | default (false) | OTHER_ERROR |
<exceptionClass> : <message> (" → ') |
— |
On revert, result.message does not carry the original reason string; you must decode it from constant_result[0]: skip the leading 4-byte 08c379a0 selector and ABI-decode the remaining bytes as string.