Skip to main content
Glama

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
NameRequiredDescriptionDefault
api_keyYesYour HederaIntel API key
contract_idYesHedera contract ID (e.g. 0.0.123456) or EVM address (0x...)
function_nameYesContract function name to call (e.g. balanceOf, totalSupply, name)
function_paramsNoOptional array of parameter values to pass to the function
abi_hintNoOptional ABI hint - common values: ERC20, ERC721, HTS

Implementation Reference

  • 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(),
      };

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/mountainmystic/hederatoolbox'

If you have feedback or need assistance with the MCP directory API, please join our Discord server