This module iterates over all the blockchain transactions and filters them by some of their fields (the from and to fields). For example, if you want to retrieve all the transactions initiated by the address 0xb6692f7ae54e89da0269c1bfd685ccdfd41d2bf7, you set the filter from = 0xb6692f7ae54e89da0269c1bfd685ccdfd41d2bf7.
Tip: This tutorial teaches you how to build a Substreams from scratch.
Remember that you can auto-generate your Substreams module by usig the code-generation tools.
Running the Substreams
First, generate the Protobuf modules and build the Rust code:
makeprotogen
makebuild
Now, you can run the Substreams:
substreams run -e mainnet.eth.streamingfast.io:443 substreams.yaml map_filter_transactions --start-block 17712038 --stop-block +3
In the previous command, you are filtering all the transactions from blocks 17712038 to 17712040, where to = 0xdac17f958d2ee523a2206206994597c13d831ec7 (the USDT smart contract address). The filters are specified in the params section of the Substreams manifest (substreams.yml):
The filters are specified as a query-encoded string (param1=value1¶m2=value2¶m3=value3). In this example, only two parameters are supported, from and to, which you can use to create filters, such as:
The module expects two inputs: the parameters string, which contains the filters, plus a raw Ethereum block. The output is the Transactions object declared in the Protobuf.
The function name, map_filter_transactions matches the name given in the Substreams manifest. Two parameters are passed: params: String, blk: Block. For Substreams, the parameter specified in the manifest is a simple String. The query-encoded format is just an abstraction that you must parse. The parse_filters_from_params parses the string and creates a TransactionFilterParams struct.
The serde_qs::from_str(¶ms) from the Serde QS Rust library parses the parameters and returns the filters struct. Then, you call the verify_filters(&filters)? function, which ensures that the filters provided are valid Ethereum addresses. If there are errors while parsing the parameters, they are collected in a substreams::errors::Error vector and returned.
Back in the main function, if the parameters parsing is correct, you start filtering the transactions:
let filters =parse_filters_from_params(params)?;// At this point, the filters are correct. If not, a Vec<substreams::errors::Error> object is returned.let transactions:Vec<Transaction> = blk.transactions() // 1..filter(|trans|apply_filter(&trans, &filters)) // 2..map(|trans|Transaction { // 3. from: Hex::encode(&trans.from), to: Hex::encode(&trans.to), hash: Hex::encode(&trans.hash), }).collect(); // 4.
The transactions() method iterates over all the successful transactions of the block.
Then, for every successful transaction, the previously parsed filters are applied.
Every transaction that complies with the filters provided is mapped into a pb::eth::transaction::v1::Transaction struct. This struct is part of the Protobuf declarations and is part of the output of the Substreams module.
Finally, all the transactions are collected into a vector of type pb::eth::transaction::v1::Transaction.