githubEdit

Protobuf

StreamingFast Substreams protobuf schemas

Protobuf overview

Substreams uses Google Protocol Buffers extensively. Protocol Buffers, also referred to as protobufs, are used as the API for data models specific to the different blockchains. Manifests contain references to the protobufs for your Substreams module.

circle-check

Learn more about the details of Google Protocol Buffers in the official documentation provided by Google.

Google Protocol Buffer Documentation

Learn more about Google Protocol Buffersarrow-up-right in the official documentation provided by Google.

Google Protocol Buffer Tutorial

Explore examples and additional learning materialarrow-up-right for Google Protocol Buffers provided by Google.

Protobuf definition for Substreams

Define a protobuf model as proto:eth.erc721.v1.Transfersarrow-up-right representing a list of ERC721 transfers.

circle-info

Note: The Transfers protobuf in the Substreams Template example is located in the proto directory.

eth/erc721/v1/erc721.proto
syntax = "proto3";

package eth.erc721.v1;

message Transfers {
  repeated Transfer transfers = 1;
}

message Transfer {
  bytes from = 1;
  bytes to = 2;
  uint64 token_id = 3;
  bytes trx_hash = 4;
  uint64 ordinal = 5;
}

View the erc721.protoarrow-up-right file in the official Substreams Template example repository.

Identifying data types

The ERC721 smart contract used in the Substreams Template example contains a Transfer event. You can use the event data through a custom protobuf.

The protobuf file serves as the interface between the module handlers and the data being provided by Substreams.

circle-check

The Substreams Template example extracts Transfer events from the Bored Ape Yacht Club smart contractarrow-up-right which is located on the Ethereum blockchain.

Several specific data types exist in the Ethereum smart contract ecosystem, some extending the ERC20 and ERC721 base modules. Complex protobufs are created and refined based on the various data types used across the different blockchains.

circle-check

Generating protobufs

The substreams CLI is used to generate the associated Rust code for the protobuf.

Notice the protogen command and Substreams manifest passed into the substreams CLI.

The pairing code is generated and saved into the src/pb/eth.erc721.v1.rsarrow-up-rightRust file.

The mod.rsarrow-up-right file located in the src/pb directory of the Substreams Template example is responsible for exporting the freshly generated Rust code.

View the mod.rsarrow-up-right file in the repository.

Protobuf and Rust optional fields

Protocol buffers define fields' type by using standard primitive data types, such as integers, booleans, and floats or a complex data type such as message, enum, oneof or map. View the full listarrow-up-right of types in the Google Protocol Buffers documentationarrow-up-right.

Any primitive data types in a message generate the corresponding Rust type,Stringarrow-up-right for string, u64 for uint64, and assign the default value of the corresponding Rust type if the field is not present in a message, an empty string for Stringarrow-up-right, 0 for integer types, false for bool.

Rust generates the corresponding message type wrapped by an Optionarrow-up-right enum type for fields referencing other complex messages. The Nonearrow-up-right variant is used if the field is not present in the message.

The Optionarrow-up-right enumarrow-up-right is used to represent the presence through Some(x)arrow-up-right or absence through Nonearrow-up-right of a value in Rust. Optionarrow-up-right allows developers to distinguish between a field containing a value versus a field without an assigned a value.

circle-info

Note: The standard approach to represent nullable data in Rust is to wrap optional values in Option<T>arrow-up-right.

The Rust matcharrow-up-right keyword is used to compare the value of an Optionarrow-up-right to a Somearrow-up-right or Nonearrow-up-right variant. Handle a type wrapped Optionarrow-up-right in Rust by using:

If you are only interested in finding the presence of a value, use the if letarrow-up-right statement to handle the Some(x)arrow-up-right arm of the matcharrow-up-right code.

If a value is present, use the .unwrap()arrow-up-right call on the Optionarrow-up-right to obtain the wrapped data. You'll need to account for these types of scenarios if you control the creation of the messages yourself or if the field is documented as always being present.

circle-info

Note: You need to be absolutely sure the field is always defined, otherwise Substreams panics and never completes, getting stuck on a block indefinitely.

PROST! is a tool for generating Rust code from protobuf definitions. Learn more about prostarrow-up-right in the project's official GitHub repository.

Learn more about Optionarrow-up-right in the official Rust documentation.

Last updated

Was this helpful?