contract_call
Execute read-only calls to Hedera smart contract functions to query data without submitting transactions or paying gas fees. Returns contract function results using 0.5 HBAR per call.
Instructions
Execute a read-only call to a Hedera smart contract function and return the result. Does not submit a transaction or cost gas. Costs 0.5 HBAR.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| api_key | Yes | Your HederaIntel API key | |
| contract_id | Yes | Hedera contract ID (e.g. 0.0.123456) or EVM address (0x...) | |
| function_name | Yes | Contract function name to call (e.g. balanceOf, totalSupply, name) | |
| function_params | No | Optional array of parameter values to pass to the function | |
| abi_hint | No | Optional ABI hint - common values: ERC20, ERC721, HTS |
Implementation Reference
- src/modules/contract/tools.js:238-360 (handler)The handler logic for 'contract_call' tool, which prepares contract information, encodes data, executes an eth_call on a mirror node, and decodes the result.
// --- contract_call --- if (name === "contract_call") { const payment = chargeForTool("contract_call", args.api_key); const base = getMirrorNodeBase(); // Fetch contract info const contractRes = await axios.get(`${base}/api/v1/contracts/${args.contract_id}`) .catch(() => ({ data: {} })); const contract = contractRes.data; const evmTarget = contract.evm_address || args.contract_id; const funcName = args.function_name; const params = args.function_params || []; // Defensively parse return_types and function_params in case they arrive as JSON strings const returnTypes = typeof args.return_types === "string" ? JSON.parse(args.return_types) : (args.return_types || []); // Build signature: use explicit abi_hint signature if provided (e.g. "balanceOf(address)") // Otherwise infer from function name and param types let signature; if (args.abi_hint && args.abi_hint.includes("(")) { // User passed a full signature as abi_hint e.g. "balanceOf(address)" signature = args.abi_hint; } else { signature = inferSignature(funcName, params); } const calldata = buildCalldata(signature, params); const selectorUsed = calldata.slice(0, 10); // Execute via mirror node eth_call let callResult = null; let callError = null; try { const callRes = await axios.post( `${base}/api/v1/contracts/call`, { data: calldata, to: evmTarget, gas: 400000, gasPrice: 0, estimate: false, } ); callResult = callRes.data; } catch (e) { callError = e.response?.data?._status?.messages?.[0]?.message || e.response?.data?.detail || e.response?.data?.message || JSON.stringify(e.response?.data) || e.message; } // Decode the result let decoded = null; if (callResult?.result && callResult.result !== "0x") { if (returnTypes && returnTypes.length > 0) { // Precise decode using ethers ABI coder try { const raw = ethers.utils.defaultAbiCoder.decode( returnTypes, callResult.result ); // Convert BigNumbers and format addresses with Hedera IDs const values = returnTypes.map((type, i) => { const val = raw[i]; if (ethers.BigNumber.isBigNumber(val)) { return { type, value: val.toString() }; } if (type === "address" || type.startsWith("address")) { const addr = val.toLowerCase(); // Convert EVM address to Hedera ID (last 4 bytes as decimal) const hederaNum = parseInt(addr.slice(-8), 16); return { type, value: addr, hedera_id: `0.0.${hederaNum}` }; } return { type, value: val.toString() }; }); decoded = { raw_hex: callResult.result, decoded_values: values, note: `Decoded using provided return_types: [${returnTypes.join(", ")}]`, }; } catch (e) { // Fall back to heuristic decoder if ethers decode fails decoded = { ...decodeResult(callResult.result), ethers_decode_error: e.message }; } } else { decoded = decodeResult(callResult.result); } } // Recent call history const resultsRes = await axios.get( `${base}/api/v1/contracts/${args.contract_id}/results?limit=10&order=desc` ).catch(() => ({ data: { results: [] } })); const results = resultsRes.data.results || []; return { contract_id: args.contract_id, evm_address: contract.evm_address || null, function_called: funcName, signature_used: signature, selector_used: selectorUsed, params_encoded: params, calldata_sent: calldata, call_result: decoded, call_error: callError, note: callError ? "Call failed — check signature or params. You can pass a full ABI signature as abi_hint e.g. 'balanceOf(address)'." : decoded ? "Call succeeded." : "Call returned empty result — function may have no return value or is write-only.", recent_call_history: results.slice(0, 5).map(r => ({ timestamp: r.timestamp, from: r.from, gas_used: r.gas_used, status: r.status, })), payment, timestamp: new Date().toISOString(), };