eth_call
https://api.triport.io/v1/polygon/rpcExecutes a read-only smart-contract call against the current (or a historical) Polygon state, returning the raw result without creating a transaction or spending gas.
eth_call runs a message call locally on a Polygon node and returns whatever
the contract's code would return, without broadcasting a transaction,
mining a block, or changing any state. It is the workhorse for reading on-chain
data: token balances (balanceOf), allowances, pool reserves, oracle prices,
and any other view/pure function or simulated state-changing call.
Because the call is executed in-memory against a chosen block, you get an
instant, gas-free answer. Use it whenever you need to read contract state or
simulate the outcome of a call before sending a real transaction. To actually
broadcast a state change, use eth_sendRawTransaction instead.
Gotchas
- The
datafield must be the ABI-encoded calldata — a 4-byte function selector followed by the 32-byte-padded arguments. Build it with a library (ethers/web3/viem) rather than by hand. - A reverting contract returns an error (with the revert reason when available), not an empty result. Always handle the error path.
- Results reflect the state at the requested block tag. Omitting the tag means
latest, which can change between calls. - Batch your reads. Calling
eth_callonce per token/account burns your rps budget fast. Route many reads through Multicall3 at0xcA11bde05977b3631167028862bE2a173976CA11(same address on Polygon) and collapse dozens of reads into a singleeth_call.
Parameters
eth_call takes two positional params: a call object and a block tag.
Call objectobjectrequiredBlock tagstringoptionallatest.Call fieldsobjecttostring (address)requiredfromstring (address)optionalmsg.sender.datastring (hex)optionalvaluestring (hex wei)optionalBlock tag valuesobjectResponse
The result is the raw, ABI-encoded return value. Here
0x…2540be400 decodes to 10000000000 — i.e. 10,000 USDC (6 decimals).
Decode it with your ABI to get a typed value.
jsonrpcstring"2.0".idnumber | stringid.resultstring (hex)0x if the function returns nothing.Errors
On failure the response carries an error object instead of result. See the
shared errors reference for the full error envelope and the
canonical meaning of each platform code.
| Code | Message | When it happens |
|---|---|---|
-32001 | trial_expired | The free-trial period for this key has ended; upgrade to continue. |
-32002 | tier_insufficient | The key's tier is below what the method requires. eth_call is available from the free tier, so this appears only if the key's scopes are restricted. Carries data.required_tier / data.upgrade_url. |
-32003 | rate_limited | Per-second rate limit for your tier exceeded; check data.retry_after_sec, back off, and retry. |
-32004 | subscription_expired | A previously active paid subscription has lapsed; renew to restore access. |
-32005 | unauthorized | Missing, invalid, or revoked API key. |
-32601 | method_unknown | The method name is misspelled or not part of the Polygon product. Carries data.chain and data.method. |
Note: Rate limiting is enforced per-second per tier with burst headroom — there is no daily quota. Exceeding your tier's rps returns
-32003(rate_limited) with adata.retry_after_sechint; back off and retry.
A reverting contract surfaces a platform error whose message includes the decoded revert reason when the contract provides one.
Examples
JavaScript (fetch)
const res = await fetch("https://api.triport.io/v1/polygon/rpc", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.TRIPORT_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
jsonrpc: "2.0",
id: 1,
method: "eth_call",
params: [
{
to: "0x3c499c542cEF5E3811e1192ce70d8cc03d5c3359",
data: "0x70a08231000000000000000000000000ab5801a7d398351b8be11c439e05c5b3259aec9b",
},
"latest",
],
}),
});
const { result } = await res.json();
console.log("raw return:", result);
console.log("balance:", BigInt(result).toString());TypeScript SDK (@triport/sdk)
import { Triport } from "@triport/sdk";
import { Interface } from "ethers";
const triport = new Triport({ apiKey: process.env.TRIPORT_API_KEY! });
const usdc = "0x3c499c542cEF5E3811e1192ce70d8cc03d5c3359";
const iface = new Interface(["function balanceOf(address) view returns (uint256)"]);
const result = await triport.polygon.rpc("eth_call", [
{ to: usdc, data: iface.encodeFunctionData("balanceOf", ["0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B"]) },
"latest",
]);
const [balance] = iface.decodeFunctionResult("balanceOf", result);
console.log(balance.toString());Python (triport-sdk)
import os
from triport import Triport
from eth_abi import decode
triport = Triport(api_key=os.environ["TRIPORT_API_KEY"])
usdc = "0x3c499c542cEF5E3811e1192ce70d8cc03d5c3359"
holder = "ab5801a7d398351b8be11c439e05c5b3259aec9b"
data = "0x70a08231" + holder.rjust(64, "0")
result = triport.polygon.rpc(
"eth_call",
[{"to": usdc, "data": data}, "latest"],
)
(balance,) = decode(["uint256"], bytes.fromhex(result[2:]))
print(balance)Notes
- Batching: wrap multiple reads in a single
eth_callto Multicall3 (0xcA11bde05977b3631167028862bE2a173976CA11). One round trip, one rps charge, consistent block snapshot across every read. - Historical reads: pass a hex block number as the second param to read state as of that block (subject to node history retention).
- Simulation: set
fromandvalueto simulate how a call would behave for a specific sender before sending it for real witheth_sendRawTransaction. - Estimating gas: to size a transaction rather than read its output, use
eth_estimateGas. - Related reads:
eth_getBalancefor native MATIC balances,eth_getCodeto confirm an address is a contract.