Making eth_calls

Learn how to perform Contract Calls (eth_calls) in EVM-compatible Substreams

EVM-compatible smart contracts are queryable, which means that you can get real-time data from the contract's internal database. In this tutorial, you will learn how to perform contract calls (eth_calls) through Substreams.

Specifically, you will query the USDT smart contract (0xdac17f958d2ee523a2206206994597c13d831ec7) to get the number of decimals used by the token. The USDT smart contract exposes a read function called decimals.

Pre-requisites

  • You have some knowledge about Substreams (modules and fundamentals).

  • You have the latest version of the CLI installed.

Querying on EthScan

You can query the decimals function by visiting EthScan.

USDT contract on EthScan

Initializing the Substreams project

  1. First, let's use substreams init to scaffold a new Substreams project that uses the USDT smart contract:

Complete the information required by the previous command, such as name of the project or smart contract to track. In the Contract address to track step, write 0xdac17f958d2ee523a2206206994597c13d831ec7, the address of the USDT smart contract.

  1. Move to the project folder and build the Substreams.

  1. Then, verify that the Substreams runs correctly. By default, it will output all the events of the smart contract.

The previous command will output the following:

Adding Calls to the Substreams

The substreams init command generates Rust structures based on the ABI of the smart contract provided. All the calls are available under the abi::contract::functions namespace of the generated code. Let's take a look.

  1. Open the project in an editor of your choice (for example, VS Code) and navigate to the lib.rs file, which contains the main Substreams code.

  2. Create a new function, get_decimals, which returns a BigInt struct:

  1. Import the abi::contract::functions::Decimals struct from the generated ABI code.

  1. Next, use the call method to make the actual eth_call by providing the smart contract address:

In this case, the call method returns a substreams::scalar::BigInt struct containing the number of decimals used in the USDT token (6).

  1. You can include this function in the map_events module just for testing purposes:

  1. To see it in action, just re-build and re-run the Substreams:

The output should be similar to the following:

Batching Calls

RPC calls add latency to your Substreams, so you should avoid them as much as possible. However, if you still have to use eth_calls, you should batch them. Batching RPC calls meaning making several calls within the same request.

In the previous USDT example, consider that you want to make three RPC calls: Decimals, Name and Symbol. Instead of creating a request for every call, you can use the substreams_ethereum::rpc::RpcBatch struct to make a single request for all the calls.

  1. In the lib.rs file, create a new function, get_calls() and initialize a batch struct.

  1. Add the calls that you want to retrieve by using the ABI of the smart contract: abi::contract::functions::Decimals, abi::contract::functions::Name and abi::contract::functions::Symbol.

The execute() method make the actual RPC call and returns an array of responses. In this case, the array will have 3 responses, one for each call made.

The order used for the response is the same as the order of addition to the request. In this example, responses[0] contains Decimals, responses[1] contains Name, and response[2] contains Symbol.

  1. Decode the Decimals response using the ABI.

  1. Then, do the same for Name and Symbol.

  1. Build and run the Substreams.

You should see an output similar to the following:

Last updated

Was this helpful?