# read_contract_multicall

Executes a table of encoded contract reads through Multicall3 and returns one diagnostic row per input call. Results separately report batch execution and target-call success.

Canonical HTML: <https://determica.com/docs/functions/read_contract_multicall>

- Kind: table function
- Category: [Chain reads](/docs/category/chain-reads.md)
- Tags: EVM, RPC, Live, Multicall, Contract Read

Executes a table of encoded contract reads through Multicall3 and returns one diagnostic row per input call. Results separately report batch execution and target-call success.

## Overload 1: read_contract_multicall(TABLE)

```sql
read_contract_multicall(TABLE)
```

Accepts a relation of encoded calls and applies named or JSON batching options to the complete input.

- Kind: table
- Execution context: Live RPC
- Behavior: Immediate Read
- Execution modes: Transport Rpc
- Input shape: Relation
- Planning contract: Runtime
- Cardinality: Zero Or More
- Determinism: External State
- Return shape: Table
- Schema stability: Fixed
- Stability: Stable
- Side effects: network_io

### Requirements

- rpc_transport
- rpc

### Risks

- provider_limits
- chain_reorganization

### Inputs

- `calls_table` (TABLE; required; positional) — Relation containing one encoded contract read per row.
- `max_calls_per_batch` (UBIGINT; optional; named) — Maximum number of input calls in one Multicall3 aggregate3 request.
- `multicall3_address` (ADDRESS; optional; named) — Override the default Multicall3 contract address for the selected chain.
- `max_calldata_bytes` (UBIGINT; optional; named) — Maximum encoded aggregate3 calldata size before a batch is split.
- `allow_failure` (BOOLEAN; optional; named) — Default aggregate3 allowFailure value when an input row does not provide allow_failure.
- `fallback` (VARCHAR; optional; named) — Behavior after batch failure: none returns the failure; scalar_on_batch_failure retries calls individually.
- `chunk_retry` (VARCHAR; optional; named) — Retry strategy after a failed batch: bisect recursively splits the batch; none returns the batch failure.
- `verify_code` (BOOLEAN; optional; named) — When true, require deployed code at the selected Multicall3 address before batching. This checks code presence, not Multicall3 bytecode identity.
- `options` (JSON; optional; named) — JSON object containing the same batch size, retry, fallback, verification, and address settings.

### Relation input

Each row describes one raw contract read. source_id is optional but recommended for stable result joins.

### Relation columns

- `source_id` (VARCHAR) — Stable caller-provided identifier for the input row. Optional
- `client` (CLIENT) — Readable EVM state for the batch. Required
- `to_address` (ADDRESS) — Contract address to call. Required
- `call_data` (BYTES) — ABI-encoded call data. Required
- `block_number` (BIGINT) — Optional block number for historical reads. Optional
- `block_tag` (VARCHAR) — Optional block tag such as latest or finalized. Optional
- `allow_failure` (BOOLEAN) — Per-row aggregate3 allowFailure setting. Optional

```sql
SELECT source_id, client, to_address, call_data, block_tag FROM calls
```

### Result columns

One diagnostic row per input call

Returns raw success or revert bytes, batch diagnostics, the resolved block parameter, and identifiers that correlate each result with its input row.

- `source_id` (VARCHAR) — Caller-provided identifier copied from the input row.
- `input_row_index` (UBIGINT) — Zero-based position of the call in the input relation.
- `ok` (BOOLEAN) — Whether client resolution and batch execution completed for this input.
- `call_success` (BOOLEAN) — Whether the target contract call itself succeeded.
- `return_data` (BYTES) — Raw return bytes for a successful target call.
- `revert_data` (BYTES) — Raw revert bytes for a failed target call, when available.
- `status` (VARCHAR) — Machine-readable outcome for the input call.
- `error_message` (VARCHAR) — Human-readable batch, transport, validation, or call failure detail.
- `batch_id` (UBIGINT) — Identifier shared by calls attempted in the same logical batch.
- `batch_index` (UBIGINT) — Zero-based position of the call within its batch.
- `batch_size` (UBIGINT) — Number of calls attempted in the batch.
- `chunk_index` (UBIGINT) — Zero-based retry chunk containing the call.
- `multicall3_address` (ADDRESS) — Multicall3 contract address used for the batch.
- `block_parameter` (VARCHAR) — Resolved block number, tag, or hash used for the state read.
- `elapsed_ms` (UBIGINT) — Elapsed execution time for the call's batch or fallback attempt, in milliseconds.
- `fallback_used` (BOOLEAN) — Whether the result came from scalar fallback after a batch failure.

One diagnostic row per input call. Returns raw success or revert bytes, batch diagnostics, the resolved block parameter, and identifiers that correlate each result with its input row.

## Guidance

### Prepare and decode batched reads

Provide encoded call_data and a CLIENT for every input row. Use source_id to join results back to the source relation.

Decode return_data only after filtering on both ok and call_success. Keep status, error_message, and revert_data when investigating failures.

- Use read_contract when one ABI-decoded scalar read is sufficient.
- Use call_decode to turn successful raw return_data into typed columns.
- Provide block_number, block_tag, or block_parameter to pin the state read.

### Understand client execution

Live clients execute Multicall3 against the selected RPC state. Attached pinned clients use a hash-pinned remote batch with a canonicality requirement.

Detached pinned clients and execution clients evaluate each read against their isolated local state.

### Control retries and fallback

Batch limits split large inputs before execution. chunk_retry controls retries of failed batches, while fallback controls whether failed batches are retried as individual calls.

By default, batches execute without a code-presence preflight. Set verify_code := true to require deployed code before use; verification does not authenticate the Multicall3 bytecode.

## Additional examples

```sql
-- Decode WETH and USDC decimals from one Multicall3 batch
WITH erc20_abi AS (
  SELECT '[{
    "type": "function",
    "name": "decimals",
    "stateMutability": "view",
    "inputs": [],
    "outputs": [{ "name": "decimals", "type": "uint8" }]
  }]'::JSON AS abi
),
calls AS (
  SELECT
    token.symbol AS source_id,
    $client AS client,
    token.address AS to_address,
    encode_function_data(erc20_abi.abi, 'decimals') AS call_data,
    'finalized' AS block_tag
  FROM erc20_abi,
  (VALUES
    ('weth-decimals', '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2'::ADDRESS),
    ('usdc-decimals', '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48'::ADDRESS)
  ) AS token(symbol, address)
),
raw_results AS (
  SELECT source_id, return_data
  FROM read_contract_multicall((
    SELECT source_id, client, to_address, call_data, block_tag
    FROM calls
  ), max_calls_per_batch := 100)
  WHERE ok AND call_success AND status = 'ok'
)
SELECT source_id, decoded.decimals::UTINYINT AS decimals
FROM raw_results
CROSS JOIN LATERAL call_decode(
  '[{"type":"function","name":"decimals","inputs":[],"outputs":[{"name":"decimals","type":"uint8"}]}]'::JSON,
  'decimals',
  return_data
) AS decoded
ORDER BY source_id;
```

## Related functions

- [encode_function_data](/docs/functions/encode_function_data.md) — Usually Before
- [read_contract](/docs/functions/read_contract.md) — Alternative
- [call_decode](/docs/functions/call_decode.md) — Usually After
