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.
BigInt
now has a new methoddiv_rem
that performs division by anotherBigInt
and returns the quotient and the remainder.
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