waterpump-evm-pool-sdk 0.1.0

EVM pool SDK — viewers, infusers, harvesters, swappers for Uniswap V3/V4, PancakeSwap, Slipstream, Shadow, Algebra
Documentation
/// Macro to generate PoolViewer trait implementation and helper methods for V3
/// two-pool viewers Assumes the struct has pool_a() and pool_b() methods
/// returning &(V3PoolKey, Address)
#[macro_export]
macro_rules! impl_v3_two_pool_viewer {
    ($struct_name:ident) => {
        impl $struct_name {
            /// Get slot0 data and calculate prices for both pools
            #[tracing::instrument(skip(self))]
            pub async fn get_prices(&self, block_id: Option<alloy::eips::BlockId>) -> anyhow::Result<(uniswap_sdk_core::prelude::Price<uniswap_sdk_core::prelude::Currency, uniswap_sdk_core::prelude::Currency>, uniswap_sdk_core::prelude::Price<uniswap_sdk_core::prelude::Currency, uniswap_sdk_core::prelude::Currency>)> {
                let slot0s = self.slot0s(block_id).await?;
                let slot0_a = &slot0s[0];
                let slot0_b = &slot0s[1];

                let price_a = $crate::pool_viewers::v3::utils::calculate_price_from_sqrt_price_x96(
                    slot0_a.sqrtPriceX96,
                    self.pool_a().0.token_a.clone(),
                    self.pool_a().0.token_b.clone(),
                );
                let price_b = $crate::pool_viewers::v3::utils::calculate_price_from_sqrt_price_x96(
                    slot0_b.sqrtPriceX96,
                    self.pool_b().0.token_a.clone(),
                    self.pool_b().0.token_b.clone(),
                );

                tracing::debug!(
                    pool_a_token_a = ?self.pool_a().0.token_a.address(),
                    pool_a_token_b = ?self.pool_a().0.token_b.address(),
                    pool_a_price = %price_a.to_significant(8, None).unwrap_or_else(|_| "N/A".to_string()),
                    pool_b_token_a = ?self.pool_b().0.token_a.address(),
                    pool_b_token_b = ?self.pool_b().0.token_b.address(),
                    pool_b_price = %price_b.to_significant(8, None).unwrap_or_else(|_| "N/A".to_string()),
                    "Individual prices calculated"
                );

                Ok((price_a, price_b))
            }

            /// Merge two pool prices based on how the pools are connected
            #[tracing::instrument(skip(self, price_a, price_b))]
            pub fn merge_prices(&self, price_a: &uniswap_sdk_core::prelude::Price<uniswap_sdk_core::prelude::Currency, uniswap_sdk_core::prelude::Currency>, price_b: &uniswap_sdk_core::prelude::Price<uniswap_sdk_core::prelude::Currency, uniswap_sdk_core::prelude::Currency>) -> anyhow::Result<uniswap_sdk_core::prelude::Price<uniswap_sdk_core::prelude::Currency, uniswap_sdk_core::prelude::Currency>> {
                $crate::common::price_utils::merge_two_pool_prices(&self.pool_a().0, &self.pool_b().0, price_a, price_b)
            }

            /// Get the current tick from pool_a
            #[tracing::instrument(skip(self))]
            pub async fn tick(&self, block_id: Option<alloy::eips::BlockId>) -> anyhow::Result<alloy::primitives::aliases::I24> {
                let slot0s = self.slot0s(block_id).await?;
                let tick = slot0s[0].tick;
                tracing::debug!(tick = ?tick, "Tick retrieved from pool_a");
                Ok(tick)
            }
        }

        // Generate intermediate_token() method using the separate macro
        $crate::impl_intermediate_token!($struct_name);

        #[async_trait::async_trait]
        impl $crate::traits::pool_viewer::PoolViewer for $struct_name {
            #[tracing::instrument(skip(self))]
            async fn currency0_price(&self, block_id: Option<alloy::eips::BlockId>) -> anyhow::Result<uniswap_sdk_core::prelude::Price<uniswap_sdk_core::prelude::Currency, uniswap_sdk_core::prelude::Currency>> {
                let (price_a, price_b) = self.get_prices(block_id).await?;

                let merged_price = self.merge_prices(&price_a, &price_b)?;
                tracing::debug!(
                    merged_price = %merged_price.to_significant(8, None).unwrap_or_else(|_| "N/A".to_string()),
                    "Merged price calculated"
                );
                Ok(merged_price)
            }

            #[tracing::instrument(skip(self))]
            async fn currency1_price(&self, block_id: Option<alloy::eips::BlockId>) -> anyhow::Result<uniswap_sdk_core::prelude::Price<uniswap_sdk_core::prelude::Currency, uniswap_sdk_core::prelude::Currency>> {
                let price = self.currency0_price(block_id).await?;
                let inverted_price = price.invert();
                tracing::debug!(
                    price = %inverted_price.to_significant(8, None).unwrap_or_else(|_| "N/A".to_string()),
                    "Merged price inverted"
                );
                Ok(inverted_price)
            }
        }
    };
}

/// Macro to generate PoolViewer trait implementation and helper methods for
/// Slipstream two-pool viewers Assumes the struct has pool_a() and pool_b()
/// methods returning &(SlipstreamPoolKey, Address)
#[macro_export]
macro_rules! impl_slipstream_two_pool_viewer {
    ($struct_name:ident) => {
        impl $struct_name {
            /// Get slot0 data and calculate prices for both pools
            #[tracing::instrument(skip(self))]
            pub async fn get_prices(&self, block_id: Option<alloy::eips::BlockId>) -> anyhow::Result<(uniswap_sdk_core::prelude::Price<uniswap_sdk_core::prelude::Currency, uniswap_sdk_core::prelude::Currency>, uniswap_sdk_core::prelude::Price<uniswap_sdk_core::prelude::Currency, uniswap_sdk_core::prelude::Currency>)> {
                let slot0s = self.slot0s(block_id).await?;
                let slot0_a = &slot0s[0];
                let slot0_b = &slot0s[1];

                let price_a = $crate::pool_viewers::slipstream::utils::calculate_price_from_sqrt_price_x96(
                    slot0_a.sqrtPriceX96,
                    self.pool_a().0.token_a.clone(),
                    self.pool_a().0.token_b.clone(),
                );
                let price_b = $crate::pool_viewers::slipstream::utils::calculate_price_from_sqrt_price_x96(
                    slot0_b.sqrtPriceX96,
                    self.pool_b().0.token_a.clone(),
                    self.pool_b().0.token_b.clone(),
                );

                {
                    use uniswap_sdk_core::entities::BaseCurrency;
                    tracing::debug!(
                        pool_a_token_a = ?self.pool_a().0.token_a.address(),
                        pool_a_token_b = ?self.pool_a().0.token_b.address(),
                        pool_a_price = %price_a.to_significant(8, None).unwrap_or_else(|_| "N/A".to_string()),
                        pool_b_token_a = ?self.pool_b().0.token_a.address(),
                        pool_b_token_b = ?self.pool_b().0.token_b.address(),
                        pool_b_price = %price_b.to_significant(8, None).unwrap_or_else(|_| "N/A".to_string()),
                        "Individual prices calculated"
                    );
                }

                Ok((price_a, price_b))
            }

            /// Merge two pool prices based on how the pools are connected
            #[tracing::instrument(skip(self, price_a, price_b))]
            pub fn merge_prices(&self, price_a: &uniswap_sdk_core::prelude::Price<uniswap_sdk_core::prelude::Currency, uniswap_sdk_core::prelude::Currency>, price_b: &uniswap_sdk_core::prelude::Price<uniswap_sdk_core::prelude::Currency, uniswap_sdk_core::prelude::Currency>) -> anyhow::Result<uniswap_sdk_core::prelude::Price<uniswap_sdk_core::prelude::Currency, uniswap_sdk_core::prelude::Currency>> {
                $crate::common::price_utils::merge_two_pool_prices(&self.pool_a().0, &self.pool_b().0, price_a, price_b)
            }


        }

        // Generate intermediate_token() method using the separate macro
        $crate::impl_intermediate_token!($struct_name);

        #[async_trait::async_trait]
        impl $crate::traits::pool_viewer::PoolViewer for $struct_name {
            #[tracing::instrument(skip(self))]
            async fn currency0_price(&self, block_id: Option<alloy::eips::BlockId>) -> anyhow::Result<uniswap_sdk_core::prelude::Price<uniswap_sdk_core::prelude::Currency, uniswap_sdk_core::prelude::Currency>> {
                let (price_a, price_b) = self.get_prices(block_id).await?;

                let merged_price = self.merge_prices(&price_a, &price_b)?;
                tracing::debug!(
                    merged_price = %merged_price.to_significant(8, None).unwrap_or_else(|_| "N/A".to_string()),
                    "Merged price calculated"
                );
                Ok(merged_price)
            }

            #[tracing::instrument(skip(self))]
            async fn currency1_price(&self, block_id: Option<alloy::eips::BlockId>) -> anyhow::Result<uniswap_sdk_core::prelude::Price<uniswap_sdk_core::prelude::Currency, uniswap_sdk_core::prelude::Currency>> {
                let price = self.currency0_price(block_id).await?;
                let inverted_price = price.invert();
                tracing::debug!(
                    price = %inverted_price.to_significant(8, None).unwrap_or_else(|_| "N/A".to_string()),
                    "Merged price inverted"
                );
                Ok(inverted_price)
            }
        }
    };
}