Files
tycho-protocol-sdk/substreams/ethereum-uniswap-v3/src/storage/pool_storage.rs

131 lines
5.3 KiB
Rust

use crate::storage::utils;
use tycho_substreams::prelude::{Attribute, ChangeType};
use substreams::scalar::BigInt;
use substreams_ethereum::pb::eth::v2::StorageChange;
use super::{constants::TICKS_MAP_SLOT, utils::read_bytes};
/// `StorageLocation` is a struct that represents a specific location within a contract's storage
/// associated with a name.
///
/// # Fields
///
/// * `name` - A string slice (`&str`) reference representing the unique name associated with this
/// storage location.
/// * `slot` - A fixed-size byte array `[u8; 32]` representing the slot in the contract storage
/// where this data is stored. This acts as a primary identifier for the location of the data.
/// * `offset` - A usize value indicating the offset in bytes from the start of the slot. This
/// allows for fine-grained control and access within a single slot.
/// * `number_of_bytes` - A usize value indicating the size of the data in bytes.
/// ```
#[derive(Clone)]
pub struct StorageLocation<'a> {
pub name: &'a str,
pub slot: [u8; 32],
pub offset: usize,
pub number_of_bytes: usize,
pub signed: bool,
}
pub struct UniswapPoolStorage<'a> {
pub storage_changes: &'a Vec<StorageChange>,
}
impl<'a> UniswapPoolStorage<'a> {
pub fn new(storage_changes: &'a Vec<StorageChange>) -> UniswapPoolStorage<'a> {
Self { storage_changes }
}
/// Iterates through storage changes and checks for modifications in the provided list of
/// storage locations. For each change, it compares the old and new values at the specified
/// offset and length for that location. If a change is detected, it's added to the returned
/// `Attribute` list.
///
/// Arguments:
/// locations: Vec<&StorageLocation> - A vector of references to StorageLocation objects
/// that define the slots, offsets, and lengths to be checked for changes.
///
/// Returns:
/// `Vec<Attribute>`: A vector containing Attributes for each change detected in the tracked
/// slots. Returns an empty vector if no changes are detected.
pub fn get_changed_attributes(&self, locations: Vec<&StorageLocation>) -> Vec<Attribute> {
let mut attributes = Vec::new();
// For each storage change, check if it changes a tracked slot.
// If it does, add the attribute to the list of attributes
for change in self.storage_changes {
for storage_location in locations.iter() {
// Check if the change slot matches the tracked slot
if change.key == storage_location.slot {
let old_data = read_bytes(
&change.old_value,
storage_location.offset,
storage_location.number_of_bytes,
);
let new_data = read_bytes(
&change.new_value,
storage_location.offset,
storage_location.number_of_bytes,
);
// Check if there is a change in the data
if old_data != new_data {
let value = match storage_location.signed {
true => BigInt::from_signed_bytes_be(new_data),
false => BigInt::from_unsigned_bytes_be(new_data),
};
attributes.push(Attribute {
name: storage_location.name.to_string(),
value: value.to_signed_bytes_le(),
change: ChangeType::Update.into(),
});
}
}
}
}
attributes
}
/// Iterates over a list of tick indexes and checks for modifications in the list of
/// storage changes. If a relevent change is detected, it's added to the returned `Attribute`
/// list.
///
/// Arguments:
/// ticks_idx: `Vec<&BigInt>` - A vector of references to tick indexes as BigInt objects.
///
/// Returns:
/// `Vec<Attribute>`: A vector containing Attributes for each change detected. Returns an
/// empty vector if no changes are detected.
///
/// Note: Currently, we only track the net-liquidity attribute for each tick.
pub fn get_ticks_changes(&self, ticks_idx: Vec<&BigInt>) -> Vec<Attribute> {
let mut storage_locs = Vec::new();
let mut tick_names = Vec::new();
// First, create all the names and push them into tick_names.
// We need this to keep the references to the names alive until we call
// `get_changed_attributes()`
for tick_idx in ticks_idx.iter() {
tick_names.push(format!("ticks/{}/net-liquidity", tick_idx));
}
// Then, iterate over ticks_idx and tick_names simultaneously
for (tick_idx, tick_name) in ticks_idx.iter().zip(tick_names.iter()) {
let tick_slot =
utils::calc_map_slot(&utils::left_pad_from_bigint(tick_idx), &TICKS_MAP_SLOT);
storage_locs.push(StorageLocation {
name: tick_name,
slot: tick_slot,
offset: 16,
number_of_bytes: 16,
signed: true,
});
}
self.get_changed_attributes(storage_locs.iter().collect())
}
}