call_decode

Decodes selector-free function return data into typed SQL columns defined by the function's ABI outputs.

Table function ABI

Example

Runs locally

-- Decode literal ABI return bytes for balanceOf()
WITH call_result AS (
  SELECT '0x00000000000000000000000000000000000000000000000000000000000003e8'::BYTES AS response
)
SELECT decoded.*
FROM call_result
CROSS JOIN LATERAL call_decode(
  '[{
    "type": "function",
    "name": "balanceOf",
    "outputs": [{ "name": "balance", "type": "uint256" }]
  }]'::JSON,
  'balanceOf',
  call_result.response
) AS decoded;

API reference

call_decode(JSON, VARCHAR, BYTES) #

The ABI and function selection must be constant because they define the result columns; return bytes may vary by input row.

Note: The ABI must be constant at planning time. The function selection must be constant at planning time. Return data must not include a function selector. Return-data framing is not validated.

Inputs

Name Type Use
abi JSON

Contract ABI JSON containing the selected function and outputs.

required positional
function_name VARCHAR

Constant function name or canonical signature identifying one ABI function.

required positional
data BYTES

Raw standard ABI return bytes from a contract call or stored payload. Do not include a function selector.

required positional

Input relation columns

Name Type
abi JSON

Foldable ABI JSON column that defines the output schema. Required

function_name VARCHAR

Foldable function name or canonical signature column that selects one ABI function. Required

data BYTES

Row-varying selector-free ABI return bytes. Required

The ABI and function columns must be constant across the relation. data may vary by row.

Returns

Name Type
<abi_outputs> ANY

Dynamic placeholder for ABI-derived output columns described by dynamicColumns.

...

ABI-derived output columns

Returns one column per selected function output. The ABI and function name define the exact output columns.

Columns
No fixed columns
Source
abi + function_name
Names
ABI output names, or output{index} for unnamed outputs
Types
Mapped from ABI output types

Guidance

Decode return bytes

Use call_decode after a raw contract call or when return bytes are already stored. The ABI and function selection must be constant because they determine the SQL schema.

Pass exact selector-free return bytes. The current decoder does not reject a selector prefix or trailing bytes, and incorrectly framed data can decode to an unintended value.

  • Use CROSS JOIN LATERAL when return bytes come from a table or CTE.
  • Use the relation-input form when ABI/function columns are constant and the return-data column varies.
  • Use read_contract when the RPC call and ABI decoding should happen in one helper.

Runs locally

WITH raw_response AS (
  SELECT '0x00000000000000000000000000000000000000000000000000000000000003e8'::BYTES AS data
)
SELECT decoded.*
FROM raw_response
CROSS JOIN LATERAL call_decode(
  '[{
    "type": "function",
    "name": "balanceOf",
    "outputs": [{ "name": "balance", "type": "uint256" }]
  }]'::JSON,
  'balanceOf',
  raw_response.data
) AS decoded;

Choose the right ABI primitive

Choose by byte framing and direction: selector construction, standard arguments, calldata, packed bytes, raw words, calldata decoding, and return-data decoding are distinct operations.

  • function_selector hashes already-canonical signature text exactly; function_selector_json builds a canonical signature from one JSON ABI fragment first.
  • encode_function_args emits selector-free standard ABI arguments; encode_function_data prefixes those same bytes with the 4-byte selector.
  • abi_encode_packed concatenates compact scalar encodings without standard ABI offsets, lengths, or 32-byte padding; evm_abi_word always emits exactly one padded 32-byte word.
  • decode_function_data consumes and validates selector-prefixed calldata inputs; call_decode consumes selector-free function return bytes.

Runs locally

SELECT
  function_selector('transfer(address,uint256)') AS selector,
  encode_function_data(
    '[{"type":"function","name":"transfer","inputs":[{"type":"address"},{"type":"uint256"}]}]'::JSON,
    'transfer(address,uint256)',
    '0x000000000000000000000000000000000000dEaD'::ADDRESS,
    1::UINT256
  ) AS calldata;