All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
- Enabled the 'skip_empty_output' instruction in all maps and stores by default, unless the macro is called with the keep_empty_output parameter, like this:
#[substreams::handlers::map(keep_empty_output)]
- Bumped the
prost
dependencies from 0.11 to 0.13.3
Because of code generated by "buf neoeinstein-prost" plugin and since Rust allows multiple versions of a dependency to be loaded, but we export public functions to the wasm interface, it is very important to align all the dependencies that may depend on this substreams library.
Here are the changes that you must perform in your substreams project to upgrade to v0.6.0:
- in
buf.gen.yaml
- bump
buf.build/community/neoeinstein-prost:v0.4.0
- bump
buf.build/community/neoeinstein-prost-crate:v0.4.1
- in
Cargo.toml
, update the dependencies/build_deps only for the modules that you already have:
- bump
substreams = "0.6"
- bump
prost = "0.13"
- bump
prost-types = "0.13"
- bump
prost-build = "0.13"
- bump
substreams-ethereum = "0.10"
- bump
substreams-antelope = "0.6"
- bump
substreams-database-change = "2"
- bump
substreams-entity-change = "2"
- Run
substreams build
again
10 | #[substreams::handlers::map]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `prost::message::Message` is not implemented for `MyData`
This means that you have a mismatch between the generated protobuf bindings (usually generated by the buf neoinstein-prost
and neoinstein-prost-crate
plugins) and the prost
/prost-types
/prost-build
version in one of your Cargo.toml. Make sure that your buf.gen.yaml file exists and points to the versions specified above, as well as the versions for prost libraries in every Cargo.toml.
error[E0308]: mismatched types
--> src/lib.rs:17:36
|
17 | my_data.block_timestamp = Some(blk.timestamp().to_owned());
| ---- ^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Timestamp`, found `prost_types::protobuf::Timestamp`
| |
| arguments to this enum variant are incorrect
|
= note: `prost_types::protobuf::Timestamp` and `Timestamp` have similar names, but are actually distinct types
note: `prost_types::protobuf::Timestamp` is defined in crate `prost_types`
--> /Users/you/.cargo/registry/src/index.crates.io-6f17d22bba15001f/prost-types-0.11.9/src/protobuf.rs:2296:1
|
2296 | pub struct Timestamp {
| ^^^^^^^^^^^^^^^^^^^^
note: `Timestamp` is defined in crate `prost_types`
--> /Users/you/.cargo/registry/src/index.crates.io-6f17d22bba15001f/prost-types-0.13.3/src/protobuf.rs:2253:1
|
2253 | pub struct Timestamp {
| ^^^^^^^^^^^^^^^^^^^^
= note: perhaps two different versions of crate `prost_types` are being used?
This means that one of your dependencies uses the wrong version of prost-types (0.11.9 in this example). You probably forgot to bump a dependency in one of your Cargo.toml, for example substreams-ethereum or substreams-antelope.
warning: Linking globals named 'alloc': symbol multiply defined!
error: failed to load bitcode of module "substreams-cc542fa47b990c4e.substreams.f89693ec9899ca95-cgu.05.rcgu.o":
warning: `my_project` (lib) generated 1 warning
error: could not compile `my_project` (lib) due to 1 previous error; 1 warning emitted
This means that the this module (substreams
) is linked many times with different versions. This is invalid because of the global exports, make sure that only the version 0.6.0 of substreams-rs is used by any of your dependencies.
-
Added ExprMatcher, and constructor(s) expr_matcher and ExprMatcher::new. This can be used to parse an expression once and re-used it to run multiple matches_keys against different keys:
let matcher = substreams::expr_matcher(&query); transactions.flat_map(|trx| { trx.instructions() .filter(|view| matcher.matches_keys(&vec![format!("program:{}", view.program_id().to_string())])) });
- Add skip_empty_output intrinsic (requires substreams server version v1.9.0 or later)
- Accepts
-
character in expression's parsing expect at the beginning of the expression. (Ex:-test
is failing,test-8
is not)
- The return of store type StoreSetSum which allows both summing and setting a value in a store. This is useful for storing aggregated values in a store. Note: to read deltas from this store, you will need to use the Deltas type. The delta values will come prefixed with "set:" or "sum:" depending on the operation, followed by the string representation of the value. It is up to the user to decode the values as necessary.
- Accept all characters within index keys except
-
,'
,"
,&&
,||
,)
,(
and spaces of all kind.
- Removed StoreSetSum. (Reverted changes from 0.5.16 and 0.5.15)
- Add StoreSetSum to the WRITABLE_STORE array
- Add new store type StoreSetSum which allows both summing and setting a value in a store. This is useful for storing aggregated values in a store.
- Add index keys protobuf in substreams crate
- Add
matches_keys_in_parsed_expr
function returning abool
. It returnstrue
, if the set ofkeys
provided, matches theexpression
. (Ex:expression: (key1 || key2)
, if the set of keys contains key1 or contains key2,matches_keys_in_parsed_expr(keys, expression)
returnstrue
, else returnsfalse
)
Added support for specifying let mut <param>: <Type>
when using the #[substreams::handlers::map]
macros, this enables in-place trimming of received data.
Added back some lost binary operations on BigInt
like BigInt - BigInt
for example.
In this release we add further arithmetics and logics operators possibilities on BigInt
type, namely:
BitAnd
BitAndAssign
BitOr
BitOrAssign
BitXor
Rem
Pow
Shl
ShlAssign
ShrAssign
The BigInt
now also has a new method div_rem
that performs division by another BigInt
and returns the quotient and the remainder.
We also improved error messages on a variety of error cases for example BigInt::to_u32
and BigInt::to_u64
will now print a much more useful message that should make it clear that the value is too big.
- Removed all internal usage of
.unwrap()
and replaced them with.expect(<message>)
or.unwrap_or_else(|| panic!(<message>))
improving the error message on different edge cases that can happen from time to time.
This release brings:
- You can now pass hexadecimal strings with the
0x
prefix to theHex::decode
function. Hexadecimal strings without the prefix are still supported.
...
use substreams::Hex;
let hex = Hex::decode("0x6e60bCdF52078A250932CF9FeC174c5F67348845")
...
- The
Hex::decode
function now accepts hexadecimal strings with the0x
prefix. Hexadecimal strings without the prefix are still supported. - Fixed warning that
std::mem::forget
was called a reference while it should receive an owned object.
This release brings:
#[substreams::handlers::map]
now handlesResult<Option<T>, Error>
andOption<T>
- Improved Error Handling
It's now possible to avoid sending back output from your mapper entirely by using Option<T>
or Result<Option<T>>
. This should be used whenever you are not returning something every block. This can make some use cases easier to "view" and comes with a small improved speed as the Protobuf encoding of an "empty" object will be avoided completely and a WASM intrinsic call will be avoided. Here an example of a Result<Option<T>, Error>
:
#[substreams::handlers::map]
fn map_transfers(blk: eth::Block) -> Result<Option<erc721::Transfers>, substreams::errors::Error> {
if some_condition {
return Ok(None);
}
Ok(Some(erc721::Transfers {
// ...
}))
}
And plain Option
:
#[substreams::handlers::map]
fn map_transfers(blk: eth::Block) -> Option<erc721::Transfers> {
if some_condition {
return None;
}
Some(erc721::Transfers {
// ...
})
}
The substreams::errors::Error
is now a plain alias to anyhow::Error
which means it much easier to create generic errors, contextualize existing one and we gain the ability to be converted from any error that implements std:error:Error
interface which is the majority of errors out there. This enables proper usage of the ?
Rust operator.
#[substreams::handlers::map]
fn map_transfers(params: String, block: Block) -> Result<Transfers, substreams::errors::Error> {
let address = Hex::decode(params)?;
// ...
}
Here, a decoding error returned by Hex::decode
will be converted to substreams::errors::Error
and an early return will happen at that point. This will make error handling and reporting much easier.
The Rust anyhow library can now be used seamlessly to quickly write ad-hoc error as well as adding context to errors. First add anyhow
as a dependency:
cargo add anyhow
Then use this code to contextualize another error:
use anyhow::Context;
#[substreams::handlers::map]
fn map_transfers(params: String, block: Block) -> Result<Transfers, substreams::errors::Error> {
let address = Hex::decode(¶ms).with_context(|| format!("invalid address '{}'", ¶ms))?;
// ...
}
This should be a seamless upgrade for the vast majority of users. This change comes at the price that Error::Unexpected("msg".to_string())
is not available anymore. Add anyhow
as a dependency to your project:
cargo add anyhow
And then convert substreams::errors:Error::Unexpected
usage with:
use anyhow::anyhow;
#[substreams::handlers::map]
fn map_transfers(block: Block) -> Result<Transfers, substreams::errors::Error> {
if block.number == 0 {
return Err(anyhow!("invalid block #{}", block.number))
}
// ...
}
-
Added support
Result<Option<>>
andOption<>
insubstreams::handlers::map
macro. -
Breaking
substreams::errors:Error
is now an alias toanyhow:Error
. This has been done for improving dealing with errors within Substreams Rust handler. If you were usingsubstreams::errors:Error::Unexpected
, now useErr(anyhow!("invalid block #{}", block.number))
(addanyhow = "1"
as a dependency of your project).
This is a re-packaging of https://github.com/streamingfast/substreams-rs/release/tag/v0.5.7 with a small removal that was actually wrong. Please see 0.5.7 release notes for highlights of previous release.
- Removed
impl<I: Iterator>
fromDeltas
, this was implemented usingpop
which returns deltas in reverse order.
- New helpers to work with
store
anddelta
keys. - Improved a bit performance of
delta
implementation. BigInt
andBigDecimal
quality of life improvements.
In this release we add various helpers to more easily decode store keys and extract meaningful information from them as well as dealing with store deltas.
In a lot of use cases, you will encode data into your keys for example user:<address>
or position:<pool>:<id>
. The new helpers make it easier than before to work with those. The Substreams default key
key format is now to use the :
segment separator to separate logical part of a key.
Import at the top of your module the use substreams::store::DeltaExt;
trait and gain access to key_segment_at_eq
, key_first_segment_eq
, key_last_segment_eq
, key_first_segment_in
and key_last_segment_in
on iterator of type Delta
.
The new key
module can then be used to extract useful part of the key:
use substreams::key;
use substreams::store::{Delta, DeltaExt, Deltas, DeltaBigDecimal};
fn db_out(store: Deltas<DeltaBigDecimal>) {
for delta in store.key_first_segment_eq("user") {
let address = key::segment_at(delta.get_key(), 1);
// Do something for this delta where the key was in format `user:<address>`
}
}
Or when filtering for multiple segments:
use substreams::key;
use substreams::store::{Delta, DeltaExt, Deltas, DeltaBigDecimal};
fn db_out(store: Deltas<DeltaBigDecimal>) {
for delta in store.key_first_segment_in(["user", "contract"]) {
// Do something for this delta where the key was in format `(user|contract):...`
}
}
The DeltaExt
trait also brings in operation_eq
and operation_not_eq
to filter Deltas
based on the actual operation.
use substreams::key;
use substreams::pb::substreams::store_delta::Operation;
use substreams::store::{Delta, DeltaExt, Deltas, DeltaBigDecimal};
fn db_out(store: Deltas<DeltaBigDecimal>) {
for delta in store
.iter()
.operation_eq(Operation::Create)
.key_first_segment_in(["user", "contract"])
.key_last_segment_eq("token0")
{
// Do something for Create delta(s) where the key was in format `(user|contract):...:token0`
}
}
- Core: Added
key
module which contains extractorsegment_at
,first_segment
,last_segment
,try_segment_at
,try_first_segment
andtry_last_segment
to extract parts of a key. - Stores: Added
store::DeltaExt
trait which contains predicateskey_segment_at_eq
,key_first_segment_eq
,key_last_segment_eq
,key_first_segment_in
,key_last_segment_in
,operation_eq
andoperation_not_eq
for filtering of delta's keys. - Stores: Added
get_key
,get_operation
to theDelta
trait, implemented for all Delta implementations. - Macros: Add support for
Option<T>
andT
as supported map output types, in addition toResult<T, ...>
. - Scalars:
BigInt
andBigDecimal
types now implement the stdDefault
trait (defaults to0
) to be able to useunwrap_or_default()
. - Scalar: Added
absolute
method onBigInt
andBigDecimal
types. - Scalar: Added
to_i32()
onBigInt
- Stores: Reduced amount of clone performed in the
store
module which should improve speed a bit.
- Stores: Breaking
Delta
trait methodnew()
has been removed, removed by a trait boundFrom<StoreDelta>
onDeltas
, shouldn't affect anyone. - Stores: Breaking
Deltas
now require the trait boundFrom<StoreDelta>
implemented for all Delta implementations, shouldn't affect anyone.
- Macros: Add
StoreSetIfNotExists*
to the list of supported stores.
- Macros: Use
std::mem::ManuallyDrop
to manage memory instead ofstd::mem::forget
for String input parameters. - Macros: Add
StoreSetIfNotExistsString
to the list of supported stores.
- Bugfix: Fixed a bug where memory was not properly freed when using String input parameters in macros.
- Added
has_at
,has_first
andhas_last
methods toStoreGet
.
- Support common-sense cross-type arithmetic operations for
BigInt
andBigDecimal
. (e.g.BigInt
+f64
->BigDecimal
)
- Fixed tests
- Macros: Add support for String input parameters
- Add
from<usize>
forBigDecimal
- Added
new
method forBigInt
andBigDecimal
. - Removed forced precision of 100 when returning a
BigDecimal
from a store. - Fixed a bug where empty byte arrays were not properly handled
- Added
BigInt::from_unsigned_bytes_be
to create theBigInt
from unsigned big endian bytes.
- Breaking Changed signature of
BigInt::from_store_bytes(bytes: Vec<u8>)
toBigInt::from_store_bytes(bytes: &[u8])
. - Breaking Changed signature of
BigDecimal::from_store_bytes(bytes: Vec<u8>)
toBigDecimal::from_store_bytes(bytes: &[u8])
. - Improved implementation of
BigDecimal::divide_by_decimals
to rely onBigDecimal
instead of a padded string. - Reduced allocation performed when using
Store::set_if_not_exists_many
,Store::set_many
andStore::add_many
functions. - Removed a bunch of unnecessary clones and removed some useless conversion which should increase overall speed of various
Store
andScalar
operations.
- Renaming
StoreSetIfNotExistsI64
,StoreI64
,DeltaI32
,DeltaI64
toStoreSetIfNotExistsInt64
,StoreInt64
,DeltaInt32
andDeltaInt64
. - Adding
StoreSetString
,StoreGetString
andStoreGetArray
typed stores.
- Adding
DeltaI32
,DeltaBool
andDeltaBytes
.
- Made Windows target(s) able to run tests when depending on
substreams
crate.
-
Abstraction of
StoreDelete
to implementdelete_prefix
andStoreNew
. -
Removing config flag
wasm32
.
- Added conditional compilation to make sure code that is linked to wasm modules can only be compiled when a wasm target is specified. Non-wasm targets will skip compiling the linked code allowing the crate to be compiled with any target.
- Renamed all
{Types}StoreGet
(e.g.:BigDecimalStoreGet
,BigIntStoreGet
, etc.) and{Types}StoreSet
(e.g.:BigDecimalStoreSet
,BigIntStoreSet
, etc.) toStoreGet{Types}
andStoreSet{Types}
- Renamed all
{Types}Delta
(e.g.:DeltaBigDecimal
,DeltaBigInt
) toDelta{Types}
- Added
StoreGetI64
andStoreSetI64
StoreGet
,StoreSet
andStoreSetIfNotExists
have all been changed from astruct
to atrait
- Multiple implementations for
StoreGet
,StoreSet
andStoreSetIfNotExists
have been added. Notably:BigDecimalStoreGet
,BigDecimalStoreSet
,BigIntStoreGet
,BigIntStoreSet
. These stores are typed, meaning the user does not need to think about the encoding and the decoding as it's done for you. The user only needs to create aBigDecimal
and store it. Storing and reading it will work out of the box for the users. No need to decode it.ProtoStoreGet<ProtobufType>
,ProtoStoreSet<ProtobufType>
andProtoStoreSetIfNotExists<ProtobufType>
. All these implementations of proto have to be typed. example:#[derive(Clone, PartialEq, ::prost::Message)] pub struct ProtobufType { [...] // your attributes defined in your proto } #[substreams::handlers::map] pub fn map_my_substreams(store: ProtoStoreGet<ProtobufType>) -> Result<[...]> { [...] }
- The previous
StoreGet
,StoreSet
andStoreSetIfNotExists
can still be used, but they need to be used asRawStoreGet
,RawStoreSet
andRawStoreSetIfNotExists
which all use bytes as input/outputs instead of a typed value. Using any of theRaw
stores has the same behaviour as before this release meaning if you have aRawStore
ofBigInt
,BigDecimal
orProtobufType
you would need to decode/encode them. - When fetching data from a typed
Store
, the user will not need to decode the returned value. Meaning a method call toget_last()
,get_at()
will all already return the decodedProtobufType
specified by the user.
- Multiple implementations for
- Custom
BigInt
andBigDecimal
have been added to be able to add synthetic sugar and make the code more readable in a substreams.- Instead of doing manipulations like
BigDecimal::from_str(your_str_representation_of_big_decimal.as_str()).unwrap()
the user can dolet bd: BigDecimal = your_str_representation_of_big_decimal.indo()
. Much clearer and less convoluted
- Instead of doing manipulations like
- Ported rust modules from github.com/streamingfast/substreams to this repository