Internal Transfers, Input Data, and Historical Balances: Ask the API Which Chains Support Them

Ganesh SwamiGanesh Swami7 min read
Explore how GoldRush exposes internal transfers, transaction input data, and historical balance support across chains.

A transaction record tells you which address called a contract. It does not always tell you where the value went during execution or what the transaction was intended to do.

Those details matter when you are building wallet histories, reconciling balances, tracking protocol activity, or analyzing contract interactions across chains.

GoldRush has expanded support for transaction input data and internal transfers across its Transactions API. It has also made these capabilities discoverable through the Chains API, allowing applications to check which features each chain supports before making a request.

Query capabilities instead of hardcoding them

Support for transaction data varies by chain and changes as coverage expands. A hardcoded chain list can become outdated, leaving your application to discover missing support when an API request fails.

The GoldRush Chains API now returns three capability flags for every chain:

  • is_internal_tx_supported: whether internal transfers are supported.

  • is_input_data_supported: whether transaction input data is supported.

  • is_historical_balances_supported: whether historical wallet balances are supported.

A chain entry looks like this:

{ "name": "eth-mainnet", "chain_id": "1", "is_internal_tx_supported": true, "is_input_data_supported": true, "is_historical_balances_supported": true }

You can query the endpoint and filter for chains that support internal transfers:

curl -s \ -H "Authorization: Bearer $GOLDRUSH_API_KEY" \ https://api.covalenthq.com/v1/chains/ \ | jq -r '.data.items[] | select(.is_internal_tx_supported) | .name'

The capability flags use the same support checks as the corresponding endpoints. This means your application can discover supported chains without maintaining a second list of rules or repeatedly sending requests that will return 501 errors.

The three capabilities are independent. A chain can support historical balances without supporting internal transfers or function selectors. Query the specific capability your application needs rather than treating chain support as a single tier.

Internal transfers: follow the value inside a transaction

An external transaction records the action submitted to the network. Once a contract executes, it can trigger additional movements of native value between addresses.

These nested movements are commonly called internal transactions, internal transfers, or traces. They are execution side effects rather than separate transactions with their own transaction hashes.

On supported chains, adding with-internal=true to a Transactions API request returns an internal_transfers array alongside the transaction data.

For example:

{ "internal_transfers": [ { "from_address": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", "to_address": "0x7a250d5630b4cf539739df2c5dacb4c659f2488d", "value": "61899385306393596", "gas_limit": "2300" } ] }

The array captures qualifying nested CALL operations that transfer non-zero native value. It excludes DELEGATECALL, STATICCALL, zero-value internal calls, contract creation, and the transaction's own top-level call.

This is not a complete execution call tree. Internal transfers tell you where native value moved inside a transaction, not every function that executed.

1. Reconcile wallet balances accurately

Consider an Ethereum transaction in block 26,193,746:

{ "tx": "0xab1f2f2706b022e42531a0f9fe1d9d32f52ab8c527f2aeab115747db19af6c96", "from": "0xaf930957c7e4e0807b0a02e913b60044bc08ce27", "to": "0x7a250d5630b4cf539739df2c5dacb4c659f2488d", "value": "0" }

The sender called the Uniswap V2 Router with a top-level transaction value of zero. Looking only at the external transaction, you cannot see any ETH arriving in the sender's wallet.

The internal transfers reveal the missing movement:

{ "internal_transfers": [ { "from": "0xc02aaa39…", "to": "0x7a250d56…", "value": "61899385306393596" }, { "from": "0x7a250d56…", "to": "0xaf930957…", "value": "61899385306393596" } ] }

The sender received approximately 0.0619 ETH. The external transaction recorded zero ETH as its top-level value.

A balance reconciliation process built exclusively on external transaction values would miss that movement. Similar discrepancies can arise around swaps, withdrawals, and claims involving contract execution.

Internal transfers provide the additional records needed to account for qualifying native-value movements.

2. Identify the actual recipient of a contract interaction

The to_address of an external transaction often identifies a router, bridge, or lending contract. That address is not necessarily the final recipient of the value.

Internal transfers let you inspect the recipient of a qualifying movement within the transaction. This helps distinguish a wallet interacting with a protocol from a wallet actually receiving value through that interaction.

The same distinction matters for bridge transactions, lending withdrawals, and aggregator routes where value passes through multiple contracts before reaching its destination.

3. Track payouts, claims, and refunds

Reward claims, payment splits, refunds, and contract-mediated payouts can move native value without creating a separate transaction initiated by the recipient.

A deposit-crediting or payment system that relies only on external transactions can miss qualifying payouts embedded in another transaction's execution.

The internal_transfers array exposes these movements with their own sender, recipient, and value fields, providing additional information for accounting and reconciliation workflows.

Input data: identify the function behind a transaction

Transaction input data contains the information sent to a contract. GoldRush exposes a useful part of this data through with-input-data=true: the input_data.method_id field.

A method ID is the first four bytes of a transaction's input. It identifies the function selector associated with the top-level call.

For example:

{ "input_data": { "method_id": "0xa9059cbb" } }

The selector 0xa9059cbb corresponds to the ERC-20 transfer(address,uint256) function.

The method ID is not the complete calldata and does not contain the decoded arguments. It tells you which function was called, not the amount transferred or the addresses passed to that function. To inspect those values, you need to decode the relevant arguments or examine the appropriate event data.

Ordinary native-token transfers with empty calldata have no method ID, so this field can be absent.

Use selectors to understand transaction intent

Once you have a transaction hash, its method ID can help classify the action without requiring full calldata decoding.

Transaction HashMethod ID
0xa9059cbbtransfer (ERC-20 token transfer)
0x095ea7b3approve (ERC-20 allowance)
0x791ac947swapExactTokensForETHSupportingFeeOnTransferTokens
0x2e1a7d4dwithdraw

These selectors provide a useful starting point for interpreting individual transactions.

Investigating approvals. The selectors 0x095ea7b3 for ERC-20 approve and 0xa22cb465 for setApprovalForAll help identify transactions that grant permissions to another address. A selector alone cannot tell you the allowance amount or whether the approval remains active. You must inspect the arguments and relevant contract state.

Understanding failed interactions. A reverted transaction does not produce successful execution logs, but its transaction record still contains its input data. The method ID can help identify which function a user attempted to call, even when that attempt failed.

For supported chains, you can request input data for a known transaction hash directly:

curl -s \ -H "Authorization: Bearer $GOLDRUSH_API_KEY" \ "https://api.covalenthq.com/v1/eth-mainnet/transaction_v2/$TX_HASH/?with-input-data=true"

Combine internal transfers and input data

For a transaction that supports both capabilities, requesting internal transfers and input data together provides two distinct views of its execution.

The external transaction identifies the contract called and the top-level value sent. Internal transfers reveal qualifying native-value movements that occurred inside the transaction. The input data's method ID identifies the top-level function called.

You can request both fields in a single API call:

curl -s \ -H "Authorization: Bearer $GOLDRUSH_API_KEY" \ "https://api.covalenthq.com/v1/eth-mainnet/transaction_v2/$TX_HASH/?with-internal=true&with-input-data=true"

In the Uniswap example above, the input selector is 0x791ac947, corresponding to swapExactTokensForETHSupportingFeeOnTransferTokens.

The internal transfers show approximately 0.0619 ETH reaching the wallet. The selector identifies the function called. Together, these two fields describe more of the transaction than the external record alone.

That distinction is useful for applications that need to understand both the movement of value and the action that triggered it.

Historical balances: know what a wallet held at a specific time

A current balance tells you what an address holds now. Historical balances answer a different question: what did that address hold at an earlier point in time?

This matters for portfolio charts, point-in-time reporting, and tax-related analysis.

The Chains API exposes is_historical_balances_supported, allowing applications to check whether historical balances are available on a given chain before making a request.

Historical balances, internal transfers, and input data solve different problems, and their support should be queried independently.

Build around capabilities that can change

For a multichain application, the workflow is straightforward:

  1. Request the supported chain list from the Chains API.

  2. Filter chains using the capability flag required for your use case.

  3. Call the relevant endpoint only on chains that support the requested feature.

  4. Refresh the capability list as coverage expands.

For example, an application that needs internal transfers can discover eligible chains programmatically:

import os import requests API_URL = "https://api.covalenthq.com/v1/chains/" API_KEY = os.environ["GOLDRUSH_API_KEY"] response = requests.get( API_URL, headers={"Authorization": f"Bearer {API_KEY}"}, timeout=30, ) response.raise_for_status() chains = response.json()["data"]["items"] supported_chains = [ chain["name"] for chain in chains if chain["is_internal_tx_supported"] ] for chain in supported_chains: print(chain)

The application no longer needs a static list of supported chains. When coverage expands, it can discover the new support without a code change.

On a chain where a requested capability is unsupported, the relevant endpoint returns a 501 response. Checking capability flags first helps avoid unnecessary requests and error handling.

Both with-internal and with-input-data are opt-in parameters. Each costs an additional 0.05 credits per item on top of the base transaction cost, on chains where that capability is supported.

Check the current support before building

Chain coverage differs by capability and evolves as more chains are integrated. Rather than relying on a static list, use the current capability flags returned by the API.

Internal transfers help you follow native value through contract execution. Input data helps identify the function called by a transaction. Historical balances provide a view of wallet holdings at a prior point in time.

The Chains API connects those capabilities to a practical multichain workflow: query what is supported, then make requests accordingly.

Enjoyed this post?

Get more Web3 insights and updates delivered weekly. Join thousands of developers staying ahead in blockchain.

Get Started

Get started with GoldRush API in minutes. Sign up for a free API key and start building.

Support

Explore multiple support options! From FAQs for self-help to real-time interactions on Discord.

Contact Sales

Interested in our professional or enterprise plans? Contact our sales team to learn more.