Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## Unreleased

### Added
- Added bidirectional conversion between `scalar::BigInt` and `substreams_ethereum::pb::eth::v2::BigInt` via the `From` trait
- Added new optional `ethereum` feature to enable Ethereum-specific functionality

## 0.6.1

- Substreams `map` or `store` input that starts with `_` doesn't generate a warning about snake cases not being respected.
Expand Down
5 changes: 5 additions & 0 deletions substreams/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ substreams-macro = { workspace = true }
thiserror = "1"
pest= "2.7.10"
pest_derive = "2.7.10"
substreams-ethereum = { version = "0.10", optional = true }

[features]
default = []
ethereum = ["substreams-ethereum"]

[dev-dependencies]
rstest = "0.19.0"
Expand Down
76 changes: 76 additions & 0 deletions substreams/src/conversions.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/// This module contains conversion traits between different BigInt implementations.
///
/// Currently, it provides conversions between:
/// - `substreams::scalar::BigInt` (aka `scalar::BigInt`)
/// - `substreams_ethereum::pb::eth::v2::BigInt` (aka `pb::BigInt`)
///
/// The conversions are implemented using the `From` and `Into` traits, which allows
/// for seamless conversion between the different types.
///
/// Note: This module is only available when the `ethereum` feature is enabled.
///
/// # Examples
///
/// ```rust
/// # #[cfg(feature = "ethereum")]
/// # {
/// use substreams::scalar::BigInt as ScalarBigInt;
/// use substreams_ethereum::pb::eth::v2::BigInt as PbBigInt;
///
/// // Convert from scalar::BigInt to pb::eth::v2::BigInt
/// let scalar_bigint = ScalarBigInt::from(42);
/// let pb_bigint: PbBigInt = scalar_bigint.into();
///
/// // Convert from pb::eth::v2::BigInt to scalar::BigInt
/// let pb_bigint = PbBigInt { bytes: vec![42] };
/// let scalar_bigint: ScalarBigInt = pb_bigint.into();
/// # }
/// ```

/// This trait is implemented for the Ethereum BigInt type to convert from scalar::BigInt.
///
/// Example:
/// ```rust
/// # #[cfg(feature = "ethereum")]
/// # {
/// use substreams::scalar::BigInt as ScalarBigInt;
/// use substreams_ethereum::pb::eth::v2::BigInt as PbBigInt;
///
/// let scalar_bigint = ScalarBigInt::from(42);
/// let pb_bigint: PbBigInt = scalar_bigint.into();
/// # }
/// ```
#[cfg(feature = "ethereum")]
impl From<crate::scalar::BigInt> for substreams_ethereum::pb::eth::v2::BigInt {
fn from(value: crate::scalar::BigInt) -> Self {
// Convert scalar::BigInt to pb::eth::v2::BigInt
// The Ethereum BigInt uses bytes representation
let bytes = value.to_signed_bytes_be();
substreams_ethereum::pb::eth::v2::BigInt { bytes }
}
}

/// This trait is implemented for the scalar::BigInt type to convert from Ethereum BigInt.
///
/// Example:
/// ```rust
/// # #[cfg(feature = "ethereum")]
/// # {
/// use substreams::scalar::BigInt as ScalarBigInt;
/// use substreams_ethereum::pb::eth::v2::BigInt as PbBigInt;
///
/// let pb_bigint = PbBigInt { bytes: vec![0x01] };
/// let scalar_bigint: ScalarBigInt = pb_bigint.into();
/// # }
/// ```
#[cfg(feature = "ethereum")]
impl From<substreams_ethereum::pb::eth::v2::BigInt> for crate::scalar::BigInt {
fn from(value: substreams_ethereum::pb::eth::v2::BigInt) -> Self {
// Convert pb::eth::v2::BigInt to scalar::BigInt
// The scalar::BigInt can be created from signed big-endian bytes
crate::scalar::BigInt::from_signed_bytes_be(&value.bytes)
}
}

#[cfg(test)]
mod tests;
65 changes: 65 additions & 0 deletions substreams/src/conversions_test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#[cfg(test)]
#[cfg(feature = "ethereum")]
mod tests {
use crate::scalar::BigInt as ScalarBigInt;
use substreams_ethereum::pb::eth::v2::BigInt as PbBigInt;

#[test]
fn test_scalar_bigint_to_pb_bigint() {
// Test conversion from scalar::BigInt to pb::eth::v2::BigInt
let scalar_bigint = ScalarBigInt::from(42);
let pb_bigint: PbBigInt = scalar_bigint.into();

// The Ethereum BigInt uses big-endian signed bytes
assert_eq!(pb_bigint.bytes, vec![42]);

// Test with a larger number
let scalar_bigint = ScalarBigInt::from(256);
let pb_bigint: PbBigInt = scalar_bigint.into();
assert_eq!(pb_bigint.bytes, vec![1, 0]);

// Test with a negative number
let scalar_bigint = ScalarBigInt::from(-42);
let pb_bigint: PbBigInt = scalar_bigint.into();
// In two's complement, -42 in a single byte would be 214 (256 - 42)
assert_eq!(pb_bigint.bytes, vec![214]);
}

#[test]
fn test_pb_bigint_to_scalar_bigint() {
// Test conversion from pb::eth::v2::BigInt to scalar::BigInt
let pb_bigint = PbBigInt { bytes: vec![42] };
let scalar_bigint: ScalarBigInt = pb_bigint.into();

// Convert back to a value we can check
assert_eq!(scalar_bigint.to_string(), "42");

// Test with a larger number
let pb_bigint = PbBigInt { bytes: vec![1, 0] };
let scalar_bigint: ScalarBigInt = pb_bigint.into();
assert_eq!(scalar_bigint.to_string(), "256");

// Test with a negative number (in two's complement)
let pb_bigint = PbBigInt { bytes: vec![214] }; // -42 in two's complement
let scalar_bigint: ScalarBigInt = pb_bigint.into();
assert_eq!(scalar_bigint.to_string(), "-42");
}

#[test]
fn test_roundtrip_conversion() {
// Test roundtrip conversion: scalar::BigInt -> pb::eth::v2::BigInt -> scalar::BigInt
let original = ScalarBigInt::from(12345);
let pb_bigint: PbBigInt = original.clone().into();
let roundtrip: ScalarBigInt = pb_bigint.into();

assert_eq!(original.to_string(), roundtrip.to_string());

// Test with a negative number
let original = ScalarBigInt::from(-12345);
let pb_bigint: PbBigInt = original.clone().into();
let roundtrip: ScalarBigInt = pb_bigint.into();

assert_eq!(original.to_string(), roundtrip.to_string());
}
}

4 changes: 4 additions & 0 deletions substreams/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,10 @@ pub mod pb;
pub mod proto;
pub mod scalar;

/// Conversions between different BigInt implementations
#[cfg(feature = "ethereum")]
pub mod conversions;

mod state;

pub mod key;
Expand Down