Repository: shuhuiluo/uniswap-v3-sdk-rs
Pull request #149: feat: refactor tick data handling and add `SimpleTickDataProvider`
Author: login: shuhuiluo
Created: 2025-04-18T08:44:46Z | Merged: 2025-04-18T09:11:07Z

Converted tick-related methods to return owned `Tick` instances instead of references for consistency and to facilitate improved data flow. Integrated a new `SimpleTickDataProvider` for on-the-fly Uniswap V3 pool data fetching, enhancing extensibility and real-time data capabilities. Updated documentation and README to reflect these changes.

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

## Summary by CodeRabbit

- **New Features**
  - Introduced a new extension module that allows fetching live tick data directly from a Uniswap V3 pool contract via on-chain calls.
  - Added a public provider for dynamic tick data retrieval, supporting both current and historical blockchain states.

- **Bug Fixes**
  - Improved test coverage for tick data providers, ensuring correct tick retrieval and navigation.

- **Documentation**
  - Updated the README to document the new extension and updated recommended dependency versions.

- **Refactor**
  - Changed several provider methods to return owned tick data instead of references, improving data handling and flexibility.
  - Updated provider methods to use asynchronous calls for tick data and bitmap retrieval.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->


Diff:
diff --git a/README.md b/README.md
index 10a3767..7955ba1 100644
--- a/README.md
+++ b/README.md
@@ -25,6 +25,8 @@ It is feature-complete with unit tests matching the TypeScript SDK.
       the state and pool for all positions of the specified owner, using RPC client, etc
     - [`price_tick_conversions`](./src/extensions/price_tick_conversions.rs) module for converting between prices and
       ticks
+    - [`simple_tick_data_provider`](./src/extensions/simple_tick_data_provider.rs) module for fetching tick data
+      from a Uniswap V3 pool contract directly via RPC calls
     - [`ephemeral_tick_data_provider`](./src/extensions/ephemeral_tick_data_provider.rs) module for fetching ticks using
       an [ephemeral contract](https://github.com/Aperture-Finance/Aperture-Lens/blob/904101e4daed59e02fd4b758b98b0749e70b583b/contracts/EphemeralGetPopulatedTicksInRange.sol)
       in a single `eth_call`
@@ -61,7 +63,7 @@ The current MSRV (minimum supported rust version) is 1.82.
 Add the following to your `Cargo.toml` file:
 
 ```toml
-uniswap-v3-sdk = { version = "3.6.0", features = ["extensions", "std"] }
+uniswap-v3-sdk = { version = "3.8.0", features = ["extensions", "std"] }
 ```
 
 ### Usage
diff --git a/src/entities/pool.rs b/src/entities/pool.rs
index b960269..38ac62f 100644
--- a/src/entities/pool.rs
+++ b/src/entities/pool.rs
@@ -233,6 +233,27 @@ impl<TP: TickDataProvider> Pool<TP> {
         })
     }
 
+    /// Returns a new pool with the given tick data provider
+    ///
+    /// ## Arguments
+    ///
+    /// * `tick_data_provider`: The new tick data provider
+    #[inline]
+    pub fn with_tick_data_provider<TP2: TickDataProvider>(
+        self,
+        tick_data_provider: TP2,
+    ) -> Pool<TP2> {
+        Pool {
+            token0: self.token0,
+            token1: self.token1,
+            fee: self.fee,
+            sqrt_ratio_x96: self.sqrt_ratio_x96,
+            liquidity: self.liquidity,
+            tick_current: TP2::Index::from_i24(self.tick_current.to_i24()),
+            tick_data_provider,
+        }
+    }
+
     async fn _swap(
         &self,
         zero_for_one: bool,
diff --git a/src/entities/tick_data_provider.rs b/src/entities/tick_data_provider.rs
index b73905f..d18518e 100644
--- a/src/entities/tick_data_provider.rs
+++ b/src/entities/tick_data_provider.rs
@@ -1,5 +1,3 @@
-#![allow(async_fn_in_trait)]
-
 use crate::prelude::*;
 use core::ops::Deref;
 
@@ -13,8 +11,8 @@ pub trait TickDataProvider: Send + Sync {
     ///
     /// * `index`: The tick to load
     ///
-    /// returns: Result<&Tick<Self::Index>, Error>
-    async fn get_tick(&self, index: Self::Index) -> Result<&Tick<Self::Index>, Error>;
+    /// returns: Result<Tick<Self::Index>, Error>
+    async fn get_tick(&self, index: Self::Index) -> Result<Tick<Self::Index>, Error>;
 
     /// Return the next tick that is initialized within a single word
     ///
@@ -42,7 +40,7 @@ where
     type Index = <<TP as Deref>::Target as TickDataProvider>::Index;
 
     #[inline]
-    async fn get_tick(&self, index: Self::Index) -> Result<&Tick<Self::Index>, Error> {
+    async fn get_tick(&self, index: Self::Index) -> Result<Tick<Self::Index>, Error> {
         self.deref().get_tick(index).await
     }
 
@@ -68,7 +66,7 @@ impl TickDataProvider for NoTickDataProvider {
     type Index = i32;
 
     #[inline]
-    async fn get_tick(&self, _: i32) -> Result<&Tick, Error> {
+    async fn get_tick(&self, _: i32) -> Result<Tick, Error> {
         Err(Error::NoTickDataError)
     }
 
diff --git a/src/extensions/ephemeral_tick_data_provider.rs b/src/extensions/ephemeral_tick_data_provider.rs
index a091b58..33f0e7c 100644
--- a/src/extensions/ephemeral_tick_data_provider.rs
+++ b/src/extensions/ephemeral_tick_data_provider.rs
@@ -89,19 +89,23 @@ mod tests {
         .await?;
         assert!(!provider.ticks.is_empty());
         provider.ticks.validate_list(TICK_SPACING);
+
         let tick = provider.get_tick(-92110).await?;
         assert_eq!(tick.liquidity_gross, 398290794261);
         assert_eq!(tick.liquidity_net, 398290794261);
+
         let (tick, initialized) = provider
             .next_initialized_tick_within_one_word(MIN_TICK_I32 + TICK_SPACING, true, TICK_SPACING)
             .await?;
         assert!(initialized);
         assert_eq!(tick, -887270);
+
         let (tick, initialized) = provider
             .next_initialized_tick_within_one_word(0, false, TICK_SPACING)
             .await?;
         assert!(initialized);
         assert_eq!(tick, 100);
+
         let provider: TickListDataProvider = provider.into();
         let tick = provider.get_tick(-92110).await?;
         assert_eq!(tick.liquidity_gross, 398290794261);
diff --git a/src/extensions/ephemeral_tick_map_data_provider.rs b/src/extensions/ephemeral_tick_map_data_provider.rs
index 45fd846..946e036 100644
--- a/src/extensions/ephemeral_tick_map_data_provider.rs
+++ b/src/extensions/ephemeral_tick_map_data_provider.rs
@@ -67,21 +67,25 @@ mod tests {
         let tick = provider.get_tick(-92110).await?;
         assert_eq!(tick.liquidity_gross, 398290794261);
         assert_eq!(tick.liquidity_net, 398290794261);
+
         let (tick, initialized) = provider
             .next_initialized_tick_within_one_word(MIN_TICK_I32 + TICK_SPACING, true, TICK_SPACING)
             .await?;
         assert_eq!(tick, -887270);
         assert!(initialized);
+
         let (tick, initialized) = provider
             .next_initialized_tick_within_one_word(-92120, true, TICK_SPACING)
             .await?;
         assert_eq!(tick, -92160);
         assert!(!initialized);
+
         let (tick, initialized) = provider
             .next_initialized_tick_within_one_word(0, false, TICK_SPACING)
             .await?;
         assert_eq!(tick, 100);
         assert!(initialized);
+
         let (tick, initialized) = provider
             .next_initialized_tick_within_one_word(110, false, TICK_SPACING)
             .await?;
diff --git a/src/extensions/mod.rs b/src/extensions/mod.rs
index 5f0db8d..73454d5 100644
--- a/src/extensions/mod.rs
+++ b/src/extensions/mod.rs
@@ -5,6 +5,7 @@ mod ephemeral_tick_map_data_provider;
 mod pool;
 mod position;
 mod price_tick_conversions;
+mod simple_tick_data_provider;
 mod state_overrides;
 mod tick_bit_map;
 mod tick_map;
@@ -14,6 +15,7 @@ pub use ephemeral_tick_map_data_provider::EphemeralTickMapDataProvider;
 pub use pool::*;
 pub use position::*;
 pub use price_tick_conversions::*;
+pub use simple_tick_data_provider::SimpleTickDataProvider;
 pub use state_overrides::*;
 pub use tick_bit_map::*;
 pub use tick_map::*;
diff --git a/src/extensions/simple_tick_data_provider.rs b/src/extensions/simple_tick_data_provider.rs
new file mode 100644
index 0000000..23e8efb
--- /dev/null
+++ b/src/extensions/simple_tick_data_provider.rs
@@ -0,0 +1,187 @@
+//! ## Simple Tick Data Provider
+//! A data provider that fetches tick data from a Uniswap V3 pool contract on the fly.
+
+use crate::prelude::*;
+use alloy::{
+    eips::{BlockId, BlockNumberOrTag},
+    network::Network,
+    providers::Provider,
+    sol,
+};
+use alloy_primitives::{aliases::I24, Address, U256};
+
+sol! {
+    #[sol(rpc)]
+    interface IUniswapV3PoolState {
+        function ticks(int24 tick)
+            external
+            view
+            returns (
+                uint128 liquidityGross,
+                int128 liquidityNet,
+                uint256 feeGrowthOutside0X128,
+                uint256 feeGrowthOutside1X128,
+                int56 tickCumulativeOutside,
+                uint160 secondsPerLiquidityOutsideX128,
+                uint32 secondsOutside,
+                bool initialized
+            );
+        function tickBitmap(int16 wordPosition) external view returns (uint256);
+    }
+}
+
+/// A data provider that fetches tick data from a Uniswap V3 pool contract on the fly.
+#[derive(Clone, Debug)]
+pub struct SimpleTickDataProvider<N, P, I = I24>
+where
+    N: Network,
+    P: Provider<N>,
+    I: TickIndex,
+{
+    pub pool: IUniswapV3PoolState::IUniswapV3PoolStateInstance<(), P, N>,
+    pub block_id: Option<BlockId>,
+    _tick_index: core::marker::PhantomData<I>,
+    _network: core::marker::PhantomData<N>,
+}
+
+impl<N, P, I> SimpleTickDataProvider<N, P, I>
+where
+    N: Network,
+    P: Provider<N>,
+    I: TickIndex,
+{
+    #[inline]
+    pub const fn new(pool: Address, provider: P, block_id: Option<BlockId>) -> Self {
+        Self {
+            pool: IUniswapV3PoolState::new(pool, provider),
+            block_id,
+            _tick_index: core::marker::PhantomData,
+            _network: core::marker::PhantomData,
+        }
+    }
+}
+
+impl<N, P, I> TickBitMapProvider for SimpleTickDataProvider<N, P, I>
+where
+    N: Network,
+    P: Provider<N>,
+    I: TickIndex,
+{
+    type Index = I;
+
+    #[inline]
+    async fn get_word(&self, index: Self::Index) -> Result<U256, Error> {
+        let block_id = self
+            .block_id
+            .unwrap_or(BlockId::Number(BlockNumberOrTag::Latest));
+        let word = self
+            .pool
+            .tickBitmap(index.to_i24().as_i16())
+            .block(block_id)
+            .call()
+            .await?;
+        Ok(U256::from(word._0))
+    }
+}
+
+impl<N, P, I> TickDataProvider for SimpleTickDataProvider<N, P, I>
+where
+    N: Network,
+    P: Provider<N>,
+    I: TickIndex,
+{
+    type Index = I;
+
+    #[inline]
+    async fn get_tick(&self, index: Self::Index) -> Result<Tick<Self::Index>, Error> {
+        let block_id = self
+            .block_id
+            .unwrap_or(BlockId::Number(BlockNumberOrTag::Latest));
+        let tick = self
+            .pool
+            .ticks(index.to_i24())
+            .block(block_id)
+            .call()
+            .await?;
+        Ok(Tick {
+            index,
+            liquidity_gross: tick.liquidityGross,
+            liquidity_net: tick.liquidityNet,
+        })
+    }
+
+    #[inline]
+    async fn next_initialized_tick_within_one_word(
+        &self,
+        tick: Self::Index,
+        lte: bool,
+        tick_spacing: Self::Index,
+    ) -> Result<(Self::Index, bool), Error> {
+        TickBitMapProvider::next_initialized_tick_within_one_word(self, tick, lte, tick_spacing)
+            .await
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::tests::*;
+    use alloy_primitives::address;
+
+    const TICK_SPACING: i32 = 10;
+
+    #[tokio::test]
+    async fn test_simple_tick_data_provider() -> Result<(), Error> {
+        let provider = SimpleTickDataProvider::new(
+            address!("88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640"),
+            PROVIDER.clone(),
+            *BLOCK_ID,
+        );
+        // [-887270, -92110, 100, 110, 22990, ...]
+        let tick = provider.get_tick(-92110).await?;
+        assert_eq!(tick.index, -92110);
+        assert_eq!(tick.liquidity_gross, 398290794261);
+        assert_eq!(tick.liquidity_net, 398290794261);
+
+        let (tick, initialized) = TickDataProvider::next_initialized_tick_within_one_word(
+            &provider,
+            MIN_TICK_I32 + TICK_SPACING,
+            true,
+            TICK_SPACING,
+        )
+        .await?;
+        assert_eq!(tick, -887270);
+        assert!(initialized);
+
+        let (tick, initialized) = TickDataProvider::next_initialized_tick_within_one_word(
+            &provider,
+            -92120,
+            true,
+            TICK_SPACING,
+        )
+        .await?;
+        assert_eq!(tick, -92160);
+        assert!(!initialized);
+
+        let (tick, initialized) = TickDataProvider::next_initialized_tick_within_one_word(
+            &provider,
+            0,
+            false,
+            TICK_SPACING,
+        )
+        .await?;
+        assert_eq!(tick, 100);
+        assert!(initialized);
+
+        let (tick, initialized) = TickDataProvider::next_initialized_tick_within_one_word(
+            &provider,
+            110,
+            false,
+            TICK_SPACING,
+        )
+        .await?;
+        assert_eq!(tick, 2550);
+        assert!(!initialized);
+        Ok(())
+    }
+}
diff --git a/src/extensions/tick_bit_map.rs b/src/extensions/tick_bit_map.rs
index 9c691f5..27d1703 100644
--- a/src/extensions/tick_bit_map.rs
+++ b/src/extensions/tick_bit_map.rs
@@ -13,11 +13,11 @@ pub type TickBitMap<I = I24> = FxHashMap<I, U256>;
 pub trait TickBitMapProvider {
     type Index: TickIndex;
 
-    /// Get a bit map word at a specific index
-    fn get_word(&self, index: Self::Index) -> U256;
+    /// Get a bitmap word at a specific index
+    async fn get_word(&self, index: Self::Index) -> Result<U256, Error>;
 
     #[inline]
-    fn next_initialized_tick_within_one_word(
+    async fn next_initialized_tick_within_one_word(
         &self,
         tick: Self::Index,
         lte: bool,
@@ -29,7 +29,7 @@ pub trait TickBitMapProvider {
             // all the 1s at or to the right of the current `bit_pos`
             // (2 << bitPos) may overflow but fine since 2 << 255 = 0
             let mask = (TWO << bit_pos) - uint!(1_U256);
-            let word = self.get_word(word_pos);
+            let word = self.get_word(word_pos).await?;
             let masked = word & mask;
             let initialized = masked != U256::ZERO;
             let msb = if initialized {
@@ -47,7 +47,7 @@ pub trait TickBitMapProvider {
             let (word_pos, bit_pos) = compressed.position();
             // all the 1s at or to the left of the `bit_pos`
             let mask = U256::ZERO - (uint!(1_U256) << bit_pos);
-            let word = self.get_word(word_pos);
+            let word = self.get_word(word_pos).await?;
             let masked = word & mask;
             let initialized = masked != U256::ZERO;
             let lsb = if initialized {
@@ -67,7 +67,7 @@ impl<I: TickIndex> TickBitMapProvider for TickBitMap<I> {
     type Index = I;
 
     #[inline]
-    fn get_word(&self, index: Self::Index) -> U256 {
-        *self.get(&index).unwrap_or(&U256::ZERO)
+    async fn get_word(&self, index: Self::Index) -> Result<U256, Error> {
+        Ok(self.get(&index).copied().unwrap_or(U256::ZERO))
     }
 }
diff --git a/src/extensions/tick_map.rs b/src/extensions/tick_map.rs
index 9cca566..ee9d2ea 100644
--- a/src/extensions/tick_map.rs
+++ b/src/extensions/tick_map.rs
@@ -37,10 +37,11 @@ impl<I: TickIndex> TickDataProvider for TickMap<I> {
     type Index = I;
 
     #[inline]
-    async fn get_tick(&self, index: Self::Index) -> Result<&Tick<Self::Index>, Error> {
+    async fn get_tick(&self, index: Self::Index) -> Result<Tick<Self::Index>, Error> {
         self.inner
             .get(&index)
             .ok_or(Error::InvalidTick(index.to_i24()))
+            .copied()
     }
 
     #[inline]
@@ -52,5 +53,6 @@ impl<I: TickIndex> TickDataProvider for TickMap<I> {
     ) -> Result<(Self::Index, bool), Error> {
         self.bitmap
             .next_initialized_tick_within_one_word(tick, lte, tick_spacing)
+            .await
     }
 }
diff --git a/src/lib.rs b/src/lib.rs
index 8d5b2aa..1cbdaa2 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,5 +1,6 @@
 #![doc = include_str!("../README.md")]
 #![cfg_attr(not(any(feature = "std", all(test, feature = "extensions"))), no_std)]
+#![allow(async_fn_in_trait)]
 #![warn(
     missing_copy_implementations,
     missing_debug_implementations,
diff --git a/src/utils/tick_list.rs b/src/utils/tick_list.rs
index 3aa65f2..fda6f80 100644
--- a/src/utils/tick_list.rs
+++ b/src/utils/tick_list.rs
@@ -111,13 +111,13 @@ impl<I: TickIndex> TickDataProvider for [Tick<I>] {
     type Index = I;
 
     #[inline]
-    async fn get_tick(&self, index: I) -> Result<&Tick<I>, Error> {
+    async fn get_tick(&self, index: I) -> Result<Tick<I>, Error> {
         let i = self.binary_search_by_tick(index)?;
         let tick = &self[i];
         if tick.index != index {
             return Err(TickListError::NotContained.into());
         }
-        Ok(tick)
+        Ok(*tick)
     }
 
     #[inline]


Reviews:
user: login: coderabbitai[bot]
body: **Actionable comments posted: 4**

<details>
<summary>♻️ Duplicate comments (1)</summary><blockquote>

<details>
<summary>src/extensions/simple_tick_data_provider.rs (1)</summary><blockquote>

`135-141`: **Follow‑up from the constructor change.**

When `new` becomes synchronous, drop the pointless `.await`:

```diff
-        let provider = SimpleTickDataProvider::new(
+        let provider = SimpleTickDataProvider::new(
             address!("88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640"),
             PROVIDER.clone(),
             *BLOCK_ID,
-        )
-        .await;
+        );
```

Yes, it’s that simple – unlike Leonard’s love life.

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: ASSERTIVE**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between baace7014541f8db7bc27e903ae41be987743a2b and 6ae81a80e4c1d49cd0ee1c25b5ae87c9e1937ed0.

</details>

<details>
<summary>📒 Files selected for processing (10)</summary>

* `README.md` (2 hunks)
* `src/entities/pool.rs` (1 hunks)
* `src/entities/tick_data_provider.rs` (3 hunks)
* `src/extensions/ephemeral_tick_data_provider.rs` (1 hunks)
* `src/extensions/ephemeral_tick_map_data_provider.rs` (1 hunks)
* `src/extensions/mod.rs` (2 hunks)
* `src/extensions/simple_tick_data_provider.rs` (1 hunks)
* `src/extensions/tick_bit_map.rs` (5 hunks)
* `src/extensions/tick_map.rs` (2 hunks)
* `src/utils/tick_list.rs` (1 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🧬 Code Graph Analysis (2)</summary>

<details>
<summary>src/entities/pool.rs (1)</summary><blockquote>

<details>
<summary>src/entities/tick.rs (3)</summary>

* `from_i24` (61-61)
* `from_i24` (89-91)
* `from_i24` (104-106)

</details>

</blockquote></details>
<details>
<summary>src/utils/tick_list.rs (3)</summary><blockquote>

<details>
<summary>src/entities/tick_data_provider.rs (3)</summary>

* `get_tick` (17-17)
* `get_tick` (45-47)
* `get_tick` (71-73)

</details>
<details>
<summary>src/extensions/tick_map.rs (1)</summary>

* `get_tick` (40-45)

</details>
<details>
<summary>src/extensions/simple_tick_data_provider.rs (1)</summary>

* `get_tick` (96-111)

</details>

</blockquote></details>

</details><details>
<summary>🪛 LanguageTool</summary>

<details>
<summary>README.md</summary>

[misspelling] ~29-~29: Use “an” instead of ‘a’ if the following word starts with a vowel sound, e.g. ‘an article’, ‘an hour’.
Context: ...odule for fetching tick data       from a Uniswap V3 pool contract directly via R...

(EN_A_VS_AN)

</details>

</details>
<details>
<summary>🪛 markdownlint-cli2 (0.17.2)</summary>

<details>
<summary>README.md</summary>

28-28: Unordered list indentation
Expected: 2; Actual: 4

(MD007, ul-indent)

</details>

</details>

</details>

<details>
<summary>🔇 Additional comments (22)</summary><blockquote>

<details>
<summary>README.md (2)</summary>

`66-66`: **Version update confirmed.**

The version update to 3.8.0 corresponds to the addition of the `SimpleTickDataProvider` functionality. This is logical and follows proper semantic versioning principles.

---

`28-29`: _🧹 Nitpick (assertive)_

**Grammatical inconsistency detected!**

The phrase "from a Uniswap V3 pool" should use "an" instead of "a" since "Uniswap" begins with a vowel sound.


```diff
-    - [`simple_tick_data_provider`](./src/extensions/simple_tick_data_provider.rs) module for fetching tick data
-      from a Uniswap V3 pool contract directly via RPC calls
+    - [`simple_tick_data_provider`](./src/extensions/simple_tick_data_provider.rs) module for fetching tick data
+      from an Uniswap V3 pool contract directly via RPC calls
```



> Likely an incorrect or invalid review comment.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 LanguageTool</summary>

[misspelling] ~29-~29: Use “an” instead of ‘a’ if the following word starts with a vowel sound, e.g. ‘an article’, ‘an hour’.
Context: ...odule for fetching tick data       from a Uniswap V3 pool contract directly via R...

(EN_A_VS_AN)

</details>
<details>
<summary>🪛 markdownlint-cli2 (0.17.2)</summary>

28-28: Unordered list indentation
Expected: 2; Actual: 4

(MD007, ul-indent)

</details>

</details>

</details>
<details>
<summary>src/extensions/mod.rs (2)</summary>

`8-8`: **Appropriate module addition.**

The addition of the `simple_tick_data_provider` module aligns with the documentation updates in README.md.

---

`18-18`: **Public export properly implemented.**

The `SimpleTickDataProvider` export makes the new functionality available to users of the crate, maintaining alphabetical ordering of exports.

</details>
<details>
<summary>src/extensions/ephemeral_tick_map_data_provider.rs (4)</summary>

`71-76`: **Test case structure is optimal.**

The test verifies forward iteration functionality from the minimum tick, checking both the returned tick value and initialization status.

---

`77-82`: **Edge case verification is satisfactory.**

This test case checks the edge case of a forward iteration that doesn't find an initialized tick, with appropriate assertions.

---

`83-88`: **Reverse iteration testing is properly implemented.**

The test verifies backward iteration from tick 0, confirming the expected initialized tick at position 100.

---

`89-93`: **Comprehensive test completion.**

The test case confirms backward iteration when no initialized tick is found within the word, asserting both the expected tick value and initialization status.

</details>
<details>
<summary>src/extensions/ephemeral_tick_data_provider.rs (4)</summary>

`93-96`: **Direct tick retrieval testing is satisfactory.**

The test verifies the correct retrieval of a specific tick by index, confirming both gross and net liquidity values.

---

`97-102`: **Forward iteration boundary case handled correctly.**

The test properly confirms the behavior of forward iteration from the minimum tick, validating both the initialization flag and tick value.

---

`103-108`: **Backward iteration from zero properly tested.**

The test verifies the next initialized tick when iterating backward from tick 0, with appropriate assertions.

---

`109-113`: **Provider conversion testing is implemented properly.**

The test validates the conversion from EphemeralTickDataProvider to TickListDataProvider and verifies that tick data remains consistent after conversion.

</details>
<details>
<summary>src/extensions/tick_map.rs (2)</summary>

`40-44`: **Method signature updated to return owned Tick instead of reference**

The change to the `get_tick` method properly aligns with the updated trait definition. The use of `.copied()` correctly converts the reference to an owned value.

---

`55-56`: **Correctly awaiting the async method call**

Adding `.await` is necessary since `next_initialized_tick_within_one_word` is now an asynchronous method. This change maintains proper asynchronous execution flow.

</details>
<details>
<summary>src/entities/pool.rs (2)</summary>

`236-255`: **New method enables dynamic tick data provider swapping**

This new method is elegant and follows the proper functional programming paradigm by taking ownership of `self` and returning a new instance. The conversion between tick index types using `from_i24` and `to_i24` is correct and maintains type safety.


The method appropriately transfers all properties from the current Pool to the new one, ensuring state consistency except for the intended provider change. The documentation is satisfactory, though a small example would have elevated it to exemplary status.

---

`252-252`: **Conversion between tick index types is correctly implemented**

The expression `TP2::Index::from_i24(self.tick_current.to_i24())` properly converts the tick index from the current provider's index type to the new provider's index type. This conversion is necessary to maintain consistency when changing tick data providers with different index type implementations.

</details>
<details>
<summary>src/utils/tick_list.rs (2)</summary>

`114-114`: **Method signature updated to return owned Tick instead of reference**

The change to return an owned `Tick<I>` aligns with the updated trait definition and improves API consistency. This modification follows the broader codebase refactoring pattern.

---

`120-120`: **Return statement correctly dereferences the tick reference**

The statement `Ok(*tick)` creates an owned copy of the tick, which is precisely what's needed to satisfy the updated method signature. This change logically follows from the signature change and ensures type correctness.

</details>
<details>
<summary>src/entities/tick_data_provider.rs (3)</summary>

`16-17`: **Core trait signature changed to return owned Tick instead of reference**

This foundational change to the `TickDataProvider` trait properly aligns with asynchronous programming patterns. Returning owned values in async contexts is generally preferred as it avoids lifetime complications and enables more flexible usage patterns.


The change enables implementations to fetch data from external sources (like blockchain) without lifetime constraints, which is particularly important for the new `SimpleTickDataProvider`.

---

`44-47`: **Updated blanket implementation to match new trait signature**

The blanket implementation correctly delegates to the underlying provider and awaits the result, maintaining proper asynchronous execution flow. This change ensures type consistency across trait implementations.

---

`70-73`: **Updated NoTickDataProvider implementation to match new trait signature**

The `NoTickDataProvider` implementation correctly returns an error result with the updated return type. This maintains backward compatibility for code using this placeholder provider.

</details>
<details>
<summary>src/extensions/simple_tick_data_provider.rs (1)</summary>

`72-84`: <details>
<summary>❓ Verification inconclusive</summary>

**Potential panic source – index truncation to `i16`.**

`word_pos` is cast to `i16` (`as_i16()`). Although current Uniswap ranges fit, the cast is unchecked and future protocol changes could overflow, silently corrupting data.

At minimum, clamp or validate before the cast and return an error instead of UB‑by‑stealth.

---

Let’s inspect the `Index` type and the `to_i24`/`as_i16` conversion in context:

---

🏁 Script executed:

```shell
#!/bin/bash
# Show the top of the file for type declarations and impls
sed -n '1,200p' src/extensions/simple_tick_data_provider.rs

# Locate the associated type Index in this file
rg -n "type Index" -A2 src/extensions/simple_tick_data_provider.rs

# Find all usages of to_i24 to see its origin
rg -n "to_i24" -n
```

Length of output: 8669

---

🏁 Script executed:

```shell
#!/bin/bash
# Display the TickBitMapProvider trait and its default impl
rg -n "pub trait TickBitMapProvider" -A50 src/extensions/tick_map.rs
```

Length of output: 70

---

🏁 Script executed:

```shell
#!/bin/bash
# Locate TickIndex trait and its methods
rg -n "pub trait TickIndex" -A30 src/entities/tick.rs

# Show implementations of to_i24()
rg -n "fn to_i24" -A10 src/entities/tick.rs

# Show implementations or default of as_i16(), if any
rg -n "as_i16" -A10 src/entities/tick.rs
```

Length of output: 1683

---

🏁 Script executed:

```shell
#!/bin/bash
# List files in src/extensions to locate TickBitMapProvider implementation
find src/extensions -maxdepth 1 -type f

# Search for TickBitMapProvider across the repo
rg -n "TickBitMapProvider"
```

Length of output: 1117

---



</details>

**Ensure safe conversion of tick index to i16**

The call  
```rust
.pool.tickBitmap(index.to_i24().as_i16())
```  
is unchecked:

- `to_i24()` uses `.unwrap()` and will panic if the index is outside ±2²³.
- `as_i16()` silently truncates a 24‑bit value to 16‑bit, corrupting data if out of ±2¹⁵.

Please validate or clamp the index before casting and return an error on out‑of‑range values.  
Location: src/extensions/simple_tick_data_provider.rs line 79

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-18T08:50:00Z

Review comments:
diff_hunk: @@ -3,6 +3,8 @@
 //! [`TickBitMapProvider::next_initialized_tick_within_one_word`] for a tick bit map that implements
 //! [`TickBitMapProvider::get_word`].
 
+#![allow(async_fn_in_trait)]
path: src/extensions/tick_bit_map.rs
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Misplaced crate‑level attribute – will not compile.**

`#![allow(async_fn_in_trait)]` must live in the crate root (`lib.rs` or `main.rs`). Inside a module it triggers `error: crate level attribute should be in the root module`.

Bazinga! Move it or delete it, otherwise the whole crate is DOA.

```diff
-#![allow(async_fn_in_trait)]
```



<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion

`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-18T08:49:59Z
author_association: CONTRIBUTOR
diff_hunk: @@ -13,11 +15,11 @@ pub type TickBitMap<I = I24> = FxHashMap<I, U256>;
 pub trait TickBitMapProvider {
     type Index: TickIndex;
 
-    /// Get a bit map word at a specific index
-    fn get_word(&self, index: Self::Index) -> U256;
+    /// Get a bitmap word at a specific index
+    async fn get_word(&self, index: Self::Index) -> Result<U256, Error>;
 
path: src/extensions/tick_bit_map.rs
user: login: coderabbitai[bot]
body: _💡 Verification agent_

<details>
<summary>❓ Verification inconclusive</summary>

**Unstable feature used without the proper gate.**

Even if you fix the attribute location, `async fn` in traits is still nightly‑only (`#![feature(async_fn_in_trait)]`). On stable Rust this explodes.

Two sane options:
1. Gate the whole crate behind the nightly feature flag.
2. Replace the async in traits with the `async_trait` crate.

Pick one before Sheldon’s patience collapses.

---



</details>

**Stop using unstable `async fn` in traits**

This won’t compile on stable Rust—`async fn` in traits is still behind `#![feature(async_fn_in_trait)]`. Pick one:

- Gate the entire crate for nightly with  
  ```rust
  #![feature(async_fn_in_trait)]
  ```
- Switch to the `async_trait` crate:  
  ```rust
  use async_trait::async_trait;

  #[async_trait]
  pub trait TickBitMap {
      /// Get a bitmap word at a specific index
      async fn get_word(&self, index: Self::Index) -> Result<U256, Error>;
  }
  ```

Location:  
- src/extensions/tick_bit_map.rs: lines 18–20

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-18T08:49:59Z
author_association: CONTRIBUTOR
diff_hunk: @@ -67,7 +69,7 @@ impl<I: TickIndex> TickBitMapProvider for TickBitMap<I> {
     type Index = I;
 
     #[inline]
-    fn get_word(&self, index: Self::Index) -> U256 {
-        *self.get(&index).unwrap_or(&U256::ZERO)
+    async fn get_word(&self, index: Self::Index) -> Result<U256, Error> {
+        Ok(*self.get(&index).unwrap_or(&U256::ZERO))
     }
path: src/extensions/tick_bit_map.rs
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**Micro‑optimisation opportunity.**

`unwrap_or` followed by deref is fine, but you can avoid the extra reference dance:

```diff
-Ok(*self.get(&index).unwrap_or(&U256::ZERO))
+Ok(self.get(&index).copied().unwrap_or(U256::ZERO))
```

Not earth‑shattering, yet cleaner.



<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
    async fn get_word(&self, index: Self::Index) -> Result<U256, Error> {
-        Ok(*self.get(&index).unwrap_or(&U256::ZERO))
+        Ok(self.get(&index).copied().unwrap_or(U256::ZERO))
    }
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-18T08:49:59Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,188 @@
+//! ## Simple Tick Data Provider
+//! A data provider that fetches tick data from a Uniswap V3 pool contract on the fly.
+
+use crate::prelude::*;
+use alloy::{
+    eips::{BlockId, BlockNumberOrTag},
+    network::Network,
+    providers::Provider,
+    sol,
+};
+use alloy_primitives::{aliases::I24, Address, U256};
+
+sol! {
+    #[sol(rpc)]
+    interface IUniswapV3PoolState {
+        function ticks(int24 tick)
+            external
+            view
+            returns (
+                uint128 liquidityGross,
+                int128 liquidityNet,
+                uint256 feeGrowthOutside0X128,
+                uint256 feeGrowthOutside1X128,
+                int56 tickCumulativeOutside,
+                uint160 secondsPerLiquidityOutsideX128,
+                uint32 secondsOutside,
+                bool initialized
+            );
+        function tickBitmap(int16 wordPosition) external view returns (uint256);
+    }
+}
+
+/// A data provider that fetches tick data from a Uniswap V3 pool contract on the fly.
+#[derive(Clone, Debug)]
+pub struct SimpleTickDataProvider<N, P, I = I24>
+where
+    N: Network,
+    P: Provider<N>,
+    I: TickIndex,
+{
+    pub pool: IUniswapV3PoolState::IUniswapV3PoolStateInstance<(), P, N>,
+    pub block_id: Option<BlockId>,
+    _tick_index: core::marker::PhantomData<I>,
+    _network: core::marker::PhantomData<N>,
+}
+
+impl<N, P, I> SimpleTickDataProvider<N, P, I>
+where
+    N: Network,
+    P: Provider<N>,
+    I: TickIndex,
+{
+    #[inline]
+    pub async fn new(pool: Address, provider: P, block_id: Option<BlockId>) -> Self {
+        Self {
+            pool: IUniswapV3PoolState::new(pool, provider),
+            block_id,
+            _tick_index: core::marker::PhantomData,
+            _network: core::marker::PhantomData,
+        }
+    }
path: src/extensions/simple_tick_data_provider.rs
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**`new` is needlessly async – you’re awaiting thin air.**

The constructor only builds a struct. Making it `async` forces every caller to poll a useless future.

```diff
-    pub async fn new(pool: Address, provider: P, block_id: Option<BlockId>) -> Self {
+    pub fn new(pool: Address, provider: P, block_id: Option<BlockId>) -> Self {
```

Kill the `.await` in tests and elsewhere accordingly. Your CPU will thank you.



<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
    pub fn new(pool: Address, provider: P, block_id: Option<BlockId>) -> Self {
        Self {
            pool: IUniswapV3PoolState::new(pool, provider),
            block_id,
            _tick_index: core::marker::PhantomData,
            _network: core::marker::PhantomData,
        }
    }
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-18T08:50:00Z
author_association: CONTRIBUTOR

PR comments:
user: login: coderabbitai[bot]
created_at: 2025-04-18T08:44:52Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- This is an auto-generated comment: skip review by coderabbit.ai -->

> [!IMPORTANT]
> ## Review skipped
> 
> Auto reviews are disabled on base/target branches other than the default branch.
> 
> 
> 
> Please check the settings in the CodeRabbit UI or the `.coderabbit.yaml` file in this repository. To trigger a single review, invoke the `@coderabbitai review` command.
> 
> You can disable this status message by setting the `reviews.review_status` to `false` in the CodeRabbit configuration file.

<!-- end of auto-generated comment: skip review by coderabbit.ai -->
<!-- walkthrough_start -->

## Walkthrough

The changes introduce a new `SimpleTickDataProvider` module, which enables fetching Uniswap V3 tick data directly from on-chain contracts via RPC. The `TickDataProvider` and `TickBitMapProvider` traits, along with their implementations, are updated to use asynchronous methods that return owned `Tick` values instead of references. The `Pool` entity now has a method to swap its tick data provider type. Tests for various tick data providers are expanded to improve coverage. Documentation and re-exports are updated to reflect the new module and trait changes.

## Changes

| File(s)                                                                                   | Change Summary |
|------------------------------------------------------------------------------------------|---------------|
| README.md                                                                                | Added `simple_tick_data_provider` to the extensions list; updated `uniswap-v3-sdk` version in example. |
| src/entities/pool.rs                                                                     | Added `with_tick_data_provider` method to allow changing the tick data provider type of a `Pool`. |
| src/entities/tick_data_provider.rs, src/extensions/tick_map.rs, src/utils/tick_list.rs   | Changed `TickDataProvider::get_tick` to return owned `Tick` values asynchronously; updated implementations accordingly. |
| src/extensions/tick_bit_map.rs                                                           | Made `TickBitMapProvider` methods async; updated implementations to return `Result<U256, Error>`. |
| src/extensions/mod.rs                                                                    | Added and re-exported the new `simple_tick_data_provider` module and `SimpleTickDataProvider`. |
| src/extensions/simple_tick_data_provider.rs                                              | New module: Implements `SimpleTickDataProvider` for on-chain Uniswap V3 tick data access via RPC; includes Solidity interface and tests. |
| src/extensions/ephemeral_tick_data_provider.rs, src/extensions/ephemeral_tick_map_data_provider.rs | Enhanced tests with additional assertions for tick retrieval and navigation logic. |

## Sequence Diagram(s)

```mermaid
sequenceDiagram
    participant Client
    participant SimpleTickDataProvider
    participant UniswapV3PoolContract

    Client->>SimpleTickDataProvider: get_tick(index)
    SimpleTickDataProvider->>UniswapV3PoolContract: ticks(index) [RPC call]
    UniswapV3PoolContract-->>SimpleTickDataProvider: Tick data
    SimpleTickDataProvider-->>Client: Tick

    Client->>SimpleTickDataProvider: next_initialized_tick_within_one_word(tick, lte, spacing)
    SimpleTickDataProvider->>UniswapV3PoolContract: tickBitmap(wordPosition) [RPC call]
    UniswapV3PoolContract-->>SimpleTickDataProvider: Bitmap word
    SimpleTickDataProvider-->>Client: (tick, initialized)
```

## Possibly related PRs

- shuhuiluo/uniswap-v3-sdk-rs#96: Introduced async `TickBitMapProvider` traits, which are now used by the new `SimpleTickDataProvider`.
- shuhuiluo/uniswap-v3-sdk-rs#148: Converted `TickDataProvider` methods to async, paving the way for these further changes and new provider.
- shuhuiluo/uniswap-v3-sdk-rs#80: Refactored `TickDataProvider` for modularity, related to trait-level changes in this PR.

## Poem

> Ticks on the chain, now fetched with flair,  
> Through async calls, the data's there.  
> Providers swapped, the pool’s anew,  
> With tests that check each ticky view.  
> Sheldon says: “Bazinga! It’s true—  
> The code is smarter, and so are you.”  
> 🧑‍🔬🦠

<!-- walkthrough_end -->
<!-- internal state start -->


<!-- DwQgtGAEAqAWCWBnSTIEMB26CuAXA9mAOYCmGJATmriQCaQDG+Ats2bgFyQAOFk+AIwBWJBrngA3EsgEBPRvlqU0AgfFwA6NPEgQAfACgjoCEYDEZyAAUASpETZWaCrKNwSPbABsvkCiQBHbGlcSHFcLzpIACIAMxJqLn9YtDF8PnEGAGtIWmo0SFhMWi94DCJ0DHo0WnoAAwBleGZuSOh4bIARfKsKfAl4JQo66Mg5SGxESntYbFn4L2x8Hj7ufCnkBiLy6TDl3FgPAFUMJAB3NG5IADUAZkgGzoBpSBtJ0P81xHV02Q0YQ6QZjaLDMRTwWIdajwfAYZBlBiLJQKDBSCjicphDpZMD+LzUKJsA6KZAEPwkXDYChYfBncj1drZOooOG4TAMXZlRA0Gr8WLk+L+DAcxD/OCoLaYUjoZqk5bNXj9DxMOFIGjC+TFSApBgLdQEsYUmh8PJs7VeWmQM7qBBYA7KxQkARoKb/ACCtXUMIwaB8sgANGFAbYWbg+rRsCL0JByGdII0FW1sd02b1+oNKMylN8iPTtel+BgwPawLEvPJ4rgtmUKvh+Sdzpcbvc1vhfKa0GKEMgarQvbDIGRtlH7YOAB7q75qUq4eR1oMeR4vLUObhrdHIfy+4vNDwdxiXFR68TSLt7/AMRzsaEDrU2ACibs6AFl79qFrsilJ0F5EMsBCQZATNwppRGSySRGIC5TDGJBxvE1BUrsWqSjsooAh4IaoBmGDiJCUQujGjgAXwACMAAsACclT0KgqQilOkToKEo6KgMSj0AA4uoAAS2ACJARw2AAMho5iWAAwiwbC4cgDhOC4biAkwrDsMgA4HKg3DeL4/hBCE6D+ECZTNL6Z44MSFCBogszzIs+CBnpwTcgR5IDHB2p9MwC4TFMfBMEMKhqGy8D/AAklg/iIGscIkIGAXKKo+rwAA5D2eAsAS9CILILneakWQYLSkS0KQ9Cjv47lxk5BlamG8BEKQ/jVG58AeYqDHmWgGXAjQ9CfOWLJ9gwWVBtQkBSUoNhBeoKA9lgCL+DJbK6SQVX2LlNDeQc420PguxFR8JC4mtbVxipzDqKSRShF+Hi+lutDyABQGVWddCBnVN0+W9HkXVqqCwoNlytFCAhMWchxYN1BC9R0rVwT2RncN1Uy0P8AByyyxFS9omkgl6IN8sKBvEdDOtkgYFr2/Y+r4F1qValAeGxGa0XagIHI9YkGPoxjgFAZD0POMOEKQ5BUH1CiqbhXC8PwwiiOIUgyPICVUElmjaLoYCGCYUDivC80WWLZDKFLDOy34aBxvJwIuGMauOhrwVaDofP86YBgPk+r4aMwtAcAY0QhwYFiQG6YXEGbktRHbzhzvyqGkIgSkeD7L5vhcyDYCBo1kgiSL3bBcYkBOZDE6CijeHF8bfC0kQAPqZFkjcdo3rNDHUgaQx0sDahSWy7C3uT5Lk8D+GIg2xF50YNogFxXHcPD4G2KJhqkoQDAUNhWBJB4+OhhtAtXEOEb2YH7ICpTcny46Tt6OdVNMo51GXD+wogzIIZS/jup64hYS+nLIGCqohpJCyiGiSud9X7YFOAvS4YAJC3DAIgWgWRmQMFjiyHyElnBEHwBoWGvgy5oAbh4bOwFQL0BniwGItwNAADYNAAAZRhkmiIwgAHGw6IPNxIRy8MaG8cI9g+SUIiZwoj1L8jLuuKWBZtLg3huwL00gjBY3IGJEO0QjAQDAEYRAFAGAAHo1EnkQKY1sXgNAUEQEHXRYdLCR2jhLUa8cHbzmThogwboS6eBUQwIEFJYCKHjNaA4zdsRt3yB3Po7FMxWnPrUS+PlxaUHhnUKwq8vDAGgFYPQzJEwkGWqIrsqAiRhPKmgLIuxaQSxsvAK485RyXgoEKUINiWTcnZPdKo0ZYxYmyKPM0ndpgixXuqcQwD5B9liIKdgYRZDcA8HUApAAmOo4Ujq/zEQUIZ2Tcn5KsBsopPS2TCkoTaH8vh8JeFoJsfA3A2q0NnqOdIDUyi+hXmvMuHJuChFiAWV+LdG7tM6d/NqDye4IC2HNFEaIpZ0O2oCT5RBvm+BHmUJQY5lmrPEaOIZ2Ln54tnASyYNZ4wosbvADZ5FmRajqAQWl9KsGwmgY/cyQzumTGHoCcZ5VsSjIKOMigFTkBVPCWQFQkRNjbAxZiUcI99xivxR4EWWAy5qipd03uTFeDSEoAMTEV17BshoDzcObphHm0foSwEkj8SS3tfOeR6RFF8GUaUYJFi2qp15pAZ8oTwkX0DpASABgI11BjRQd4UbAnaiwJE2A0TsixLZPE9MQwTkbK4IyLIKY0BpkSRQPQAAKKYXhYigJie3MV+bTkAEpdB6GsMczZhho0xojQmso8ZEwnPzcmHoCSMxlvbW2E55z+11GMWYv10hrG5LsV/YOodeZe3neY3C6irFgvrWOoYq7HGh2tVHDJsdsqOHtonRgCrfHuHJHs9VsDAR1FILgNNmD0A5WFCE4k7MfLrJHamI9SSN6zQBrgeEFCymALEVQnxbz6F1BsNIbwuBgAADIC3AAaCQatHAOARVxXoQM94OnpHOeWgoyRmZXPEQUAtLayRoYw8I/J2J8OEdiMR0jZdyOQEo30MtzJaM0jpFEFjEr71SkoefNcpQ0nKqoLNJQkJTgIdAYCcGmA6mhBKfB70+Y+CYHkOSvczNFnCipWSAoIGuijuzZmT6AzX5YwLUWkt47mTcjjVBIz14EPmSldlBqPpf4eCKtVCkVIxENKiI5n9EhfTOQuQkYW/J6NChFJ9BZSs7OAjC2ldeVAoJajWNM+AsyUAwd8mgaUWpSiVl3ECTAjXSnsAEc4oRIiENygkaIZ1Mi77uvRFEJR/EfWDl3ZY/RQaQ3hdzIhIyucaFcDqC6WQ/7YhYE/d+8t2Gq01qGmXLgBGiMkdJS23WrwONYdw9xy7fHrtkYo1RsTuDIOhGS958DwxICACTCeM23dv7YpId47vHAw4vOw8Xj/Gbutvuw4TjeGXtI/e8Jz7RTA3BsA/YCLq2PDrayptsHwS9uQAOy3I7J3Yekou4jt7ZdbttvQ2jx7GOWcCbHEJkT1HikLQbvGLzznS2mZgFYK0hwjIFK4J0az+SCEUmHU5sDLmvsg623+qnEOv10+h9WxnuLmdXb5+z1HmGuPZB4xb0lAvcd1Hx0tonK2ovUPJ6DvXSaaeQ6NwzyAjcuDwFuBsq3nObdPcpjj0TM6RetDF6B4tAOpeeZTz5ruwOfc7f1/7w32J6cw+D6H8PkeHu26yB9+PLunGbsMQYbd78K6P3MdwQ4bAqBeG/RmtAWbS0nvXXo89bjzZxxvQnO+PiA1PpoLfHGwoEPxnn1+kgHeuvd974erXzIqFDj6fQcYNNMTMEw80piLo/IDbGlvTJsQLPKXSJPXA5AiawOFfufw9U1o/K1D6AYIgURADapdCBoVZXUSEEaP0fLY0SAVLUoU0IrDwEeG+XAHTFAgyGLAeKsQ4HsYZHIcaAoaKUQCEeGOHPFLUK/SgerM1UoIIQYdQeQBA5yHZBcLAaA38eMcgCcWlLTGrUoAALzoF7xTTKEblhBIEbjOHSFoGZFwGtA5CtBuXmRsy6WkSJEoEQE+iJhoKpQEHwAOB8hMhmSEOAN6UpHmnKkBG/3izAmFRYNPEgAADFMUQEfJV9EUaDroPB19O9lBfA1Uyh7Nk9shhI1R/td8aJyQh5sgfDIB6DsBGDZx4C0t6l+QCgR4yQK4kIURvgXINQzwYIaYb9FoEgYI2klQqBpRWlhVv8KA2oEDoj1A7UBwLQMVgkxCOZMDuQetrVbUXVP4HUrMpFBiEs5ExwFFJsvVptVE5t/VNFlgZ8mYjJgRkQyRRwnVpEb83VJiPVpjAkZtF1RRh99Et0TFzFy5VRP529/Ct8wVgRuA+8B9x0h8nFR9L0PFJ8vEk4H1Z9OYDJF8xATNmUQhG4/DN9fRe9HjnixU99CIW9n56AU10AAFvQ/9dD0R7U0QIRZBkDDQvwYQ+BWl30eCv0TCBD4BhDaBRCbRxDJDpDZDmQwt/hnD8AqQAlODBtNJJU3czhmZUSOJlCjDVCGNDMMBtJ1CqBND7FAwEh4VgUfBaQohj9MSb9Yish8TbDqR7CRlHDmjaD+DfQqTzCLVJgij7o0ShiyiXQejQgmA0ROspdUsGj2TSRhV9IGjpB4pYRIQKBLpMR5ElZdSchHCrCWQvRjTBDgCyxGtkBgU+ADCjCEyLgKBqgBkKYshUzsoEgTE8DhTbRCxKFZCP84i+iXEBjRsNjHVhttjXUJipjhYZiglZtwgFiDAtESAdEN0DEjELjETK4rEwR0Z7FT0R8XEL0Y4viFI70Z8jB/Ehlhya4YxyEkt65WgpCD04k4TChCIXpoZUlyor41lm8rjBzTFhzV0oVIh/gpIOlpAYo+xyh3DX4mgKFxdNdS1mQLEKx3luwAklymJUBsDvUOhBp/AwBxspZfRYQiBvh1jARtVuQqVALkIBljjMZlhDC5dtR4FgSgFfACx/NIxf4fllj+TViaguyjBBEbV+t7VqyRiRsdiGz9imzDi5i2zfEoBnxT5LSOJNt1ym4tzM0dzZ1TyP44QLzFArzA0rBZjgkIKoLBS6BNs3yNyPzU8oiUU64Slt9tyAdhc65+yzy29Lz7E68ezziF1TKbihLNy60DKtc3iz0Jyx8r17BvjZy/ilJgKPI3hb5UKUA4NGYHN1KkwNctKvzb8cCh53SRl9wnofRLpODfz6ECh55F5mxfl6ZYQN4oJKVMRYQwBJR+0uS2CNMyhkIHg2xkj5BgjKAdQ1kwpMrLg7gck2wGgLUSA98bkFDlh1ogSBtNsW5EBy1gj6Um1mRtTThMQlAQoSoCDzUDQOw3N6gW4AAhdQR48a3CUiJhKa59eLKlAoYKR4q0Esz4KKNRU1I0oQkMo+AVNPYiwLZAS9eGKo2CfqigavFYLXNapaig9VbQwoNsJ8ioAoFUfK8U3pRjKgmkQFdE3wcGC8HIHCPCNqPgBMyAT0vEzEbsAgBo6A/gPgfETwiw6igwJ9IIkK2SMIGQsINTGDTbAtLa3AZ8S4LPJJJlTSrm4YcyZLNmjm7gPm4pWmi1EzSsOK6MEeM6psGQtMx2A+UoJVd9Ta7ay4dlXCcrUIMLZiHyEgyA15MYC0bIAW3mwy4Kjc4zAcKW/M+a7QRalVMecYTg/E5lbEL+EA0NAZGa/AoSsgpLAtPzMMEigshI+ABgvsFIsoBMuGWENg30P8XIQjEgIA+fHyPWskTTaw6Ld+CM0wqkkMgs/tSQsYDWq4BWo84DVm9QYW0Wxm7QQzcW8pNOP6yXVcBRHsPXWAPoIqSYAuyk6M5fLUT0/1QMQqioUcQ2wOo/U2nIMKToEmlOlIc/JVY8hIgkW+FGs2jCE+CMIC4UIuJ5FofwKGb4b8Vfa6caLkS5GZGgHyNVFEgoQqBpQSBBLK5ebpXsKKcMnEh/BQe8qCZ2s0eoxov/AZMkweqM4ui0fALIXOeMgsF0mEAeklYaNC+gPsF/LlIwAAeUdJ8AwP3uXPGXwNKCkCpiLCUGBAGX3D/CpCUOxtaqXnuBHgptVnHhf0GlxvxO6Shp1uslzgUX0OwsYCpE6WiIJs+WJp3qyFKqwA4fLL61aLEUYpTtGNG12MbOXtAt9XmO4ojkPI4uCRet+3CpIAtq12AAxkDCsEDDCkgAAF5IAwp6VzkUT7lHkg4I0oA6gbEuAWqP62rbgOqvAuqCR+MWH2rckImaAIpYaORgBy0m07HAwMY8dfH4w5HaVw08HEbYRgANr56wpaBMndBrBthYYi13xCNHkpc6gwpGUBk6gMYXcoAPQhS9Hf0888jQ60g+AwsfGKn/H+IenwcS5y0AmjHaBf7AwG1rBAwcnBguB8mEMimSmymrcXsjKEx3zM9DLA0wpxawImbQisghbObLbsa9mNKDmoiU1hmOmiYLwaspZLN4w+dnHXGXcsm3Rfc9bddenqcDsq7i8TczsxxzdXtLcUco9OMjgNkABWJhGvIXI5k58qM5v7CXXzBpixqx6Kx5hNZ5v8XUfOFZZq0lb5ppkliOAFt3IFiZ2nIvY3U7Cg6FrHNnOFyvHnB3bHQXMTOl/53pwFynP3MkvgyM+6mksFLoiQ8gRktM8F07FuTl1nMcQMW1LgAwtsWtdNaKVIGsdV2Fu7eFrDctTHDVpZ3JVJuPdFjp4xhoWq6O+q3CRq1IZq6J0J2J7q3qowsLBxOluoIakzUa3a3AelAgltFvakH5daP2yActDQFNqa4N0NjSbENmna4I/ai6tMnJb4BDGN8uON3wBNuLakZActJI3CZFg6yyvRBvPsmyyS/dGJYKRuR4ly8ciOSc9xKWTxby+Tf4tZWu9mq5qIn7UrQF0Fpk5JeVeTFDfKDaYUXu2EN03CpfEzGaqlOoRFlF+Q5YTAcZrYPuzdjNyKSt2aiodjLnYAA91F+1sTW85/B82EcGl80k9+KVwu6k2kzSDABVqQqu5kvk8+X8JYh9augCU99d/u8M7Am2Ju+Ij9SHUD5WwMbYEod281/1/uV+AAfmZGeXNnSHMiC1wmAPnDQ6/Qw5ufHeFvhJzjzneeWF6nhSJQ8nFZzEiyQkcmvb3bwayHLUfcOtvsyzviEHeHjEfe2RmdplmWIewQJDAEiCkF8GoHqgEDwDWTMBAAAG1gFaRaM9dG49s+Dm4mam0ABdZjlS6u4zuMcVy9+EO0Jmx6jwdo+GFCPKvoO5C0OMGjyVikmB2VmJeVhkjDpaEECYNdpd8cAFIFamC4L0VW+6X3WjpVuQ5W8MxUFGdOqlSgUTE4mi/o+ioY9RrYsY2RccHRqbFs44hbaAM5vW3jknL3PqTbEF9D2QlV03eHK101ttOTnPJl/POd5VtlgbqFhHfl7ls1yvR9tFr7ftH7c5y5kWy28Skytt0xMFTt7tiywNFrpu725bPjtbVj1S6lLAELu6ou8L9NSLxVsF6bggk10lLVmgHV3JfV1uQ13UcoT73FCve9y13nL7sYW1lb85HXFzu7n90LmVgD20YD7L/rj7ubmFqH7V6HvVggxuQH417HrlscMHm3CH+bzV/HrwO1wVhPRu2aQWuuyd6Knb1t1vG4g79QLty4WSqAY5pPNr4nT3MnLr27gvDH97jl0njVq3Ub+H33HrujvrmXpnOX4b63BF+t2H3ZwdMKdXLIPnNtRjtn8dKXM37gYAMKRnudXbrnqSnnr9I7tdevXspvB364p3mJV3sc3rVxT4wdry6fHyqmmw6919EktZFlpkU98777d9QlvFyjiW22gsZLJjhduTHYZd6MHLMgJQkI5LcTOoGPLIe3HHsjOoVjY9yTPMEvpN5LSvsnopJtV9+86KD9msL9jwPW7Ark+MDQJgF5OgFJkj7o59L0qQegfUnS0cBqst3cmyc68CSPyG55N1lyXkSZAvq5f+aOpG3v5W8ROoe76Vx71H+k17+djSd9OW7gfDtbwERAVcpn5u624LEzJDaDpjVLliCPnsiiA4xPcx+T0JiG2TId1A2yWipWRvxVdayNXMbHsQmzsVumTXV3ITna5i9ru4acbn7lj4id1eZuTXsjkW73ty+LfDVk7lrxjcEeUvQPCXll5DcyBHOXls9kh4CtncifNZAbyN4m8YA9zSXNjQLTC0be5yORrgnt6c9ve7bdNK70bZnFG826PAAsDkGtw0CPbAPv23HzXoZyofEdm3RmpR9+Qo4SnPB03azsA82IIyq/GT7Z5U+MZDPgZzwy287O2fZDJ5FQzmscMrgmgULlP4+C/BevDvlFEfI98AwmdN3NgUTYb9R+DIbjLb2ZD6ly0dQYTuWgABULcKai2gk478MiAoBjEX2PbnNxBpfdIdkJr6hY3c3nRSqUhBDxFX+bAUPF0karpBmA+BNQD6AdhTBnAnHbGDig8LCpxgFBATnsmNjFcCwEIIYSMmAqGF14IID6NEWwqUBrQMERNsqmxCyYfE6AArGIHiKJZ7ECAK4FMFoaZBauYCIAUKljxggTUFQHSqdWfzKl+oJAGzMUMqD8ApM9AEfn8BooB84BDFDetVy0asVUBujBSq2T3QLYCc1Sd3Jd1Jy4CKcyvA3FDiDyy8woFPTjJQNt5686ByIhgayzREa8MRPLe9sEOfaM9X4fAwQdkAEH2Dpg2NFwYkL0DuCOepiVQb+H24xJNBx3d3rrE9iCwBkIsDKO5VGiWxOA1sW2CH1drOwZoWsd2PyP1jSxLo5JR5I3F+j8kaSvSdEDrEMAGAlRTCNACQG4SkQ0A3CVhCQHIgMBSItAKiAwFoAWiSApEBgMiwEBIsjR3CAAOwMBKIToyiLcE9F0BWEuogUcqN56DBEA6o06HBBEJCxdRQAA== -->

<!-- internal state end -->
<!-- tips_start -->

---



<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=shuhuiluo/uniswap-v3-sdk-rs&utm_content=149):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Generate unit testing code for this file.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai generate unit testing code for this file.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and generate unit testing code.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information.
- If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json`

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
author_association: CONTRIBUTOR
user: login: shuhuiluo
created_at: 2025-04-18T08:45:19Z
body: @coderabbitai review
author_association: OWNER
user: login: coderabbitai[bot]
created_at: 2025-04-18T08:45:24Z
body: <!-- This is an auto-generated reply by CodeRabbit -->
<details>
<summary>:white_check_mark: Actions performed</summary>

Review triggered.

> Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

</details>
author_association: CONTRIBUTOR