kabu_node_debug_provider/
debugprovider.rs

1use std::marker::PhantomData;
2use std::sync::Arc;
3
4use alloy::eips::BlockId;
5use alloy::{
6    network::Ethereum,
7    node_bindings::{Anvil, AnvilInstance},
8    primitives::{BlockHash, BlockNumber, U64},
9    providers::{ext::DebugApi, Network, Provider, ProviderBuilder, ProviderCall, RootProvider},
10    rpc::{
11        client::{NoParams, WsConnect},
12        types::trace::geth::{GethDebugTracingCallOptions, GethDebugTracingOptions, GethTrace, TraceResult},
13        types::{BlockNumberOrTag, TransactionRequest},
14    },
15    transports::TransportResult,
16};
17use async_trait::async_trait;
18use eyre::{eyre, Result};
19use k256::SecretKey;
20
21//use crate::HttpCachedTransport;
22
23#[derive(Clone, Debug)]
24pub struct AnvilDebugProvider<PN, PA, N>
25where
26    N: Network,
27    PN: Provider<N> + Send + Sync + Clone + 'static,
28    PA: Provider<N> + Send + Sync + Clone + 'static,
29{
30    _node: PN,
31    _anvil: PA,
32    _anvil_instance: Option<Arc<AnvilInstance>>,
33    block_number: BlockNumberOrTag,
34    _n: PhantomData<N>,
35}
36
37pub struct AnvilDebugProviderFactory {}
38
39pub type AnvilDebugProviderType = AnvilDebugProvider<RootProvider<Ethereum>, RootProvider<Ethereum>, Ethereum>;
40
41impl AnvilDebugProviderFactory {
42    pub async fn from_node_on_block(node_url: String, block: BlockNumber) -> Result<AnvilDebugProviderType> {
43        let node_ws = WsConnect::new(node_url.clone());
44        let node_provider = ProviderBuilder::new().disable_recommended_fillers().connect_ws(node_ws).await?;
45
46        let anvil = Anvil::new().fork_block_number(block).fork(node_url.clone()).chain_id(1).arg("--disable-console-log").spawn();
47
48        //let anvil_layer = AnvilLayer::from(anvil.clone());
49        let anvil_url = anvil.ws_endpoint_url();
50        let anvil_ws = WsConnect::new(anvil_url.clone());
51
52        let anvil_provider = ProviderBuilder::new().disable_recommended_fillers().connect_ws(anvil_ws).await?;
53
54        let curblock = anvil_provider.get_block_by_number(BlockNumberOrTag::Latest).await?;
55
56        match curblock {
57            Some(curblock) => {
58                if curblock.header.number != block {
59                    return Err(eyre!("INCORRECT_BLOCK_NUMBER"));
60                }
61            }
62            _ => {
63                return Err(eyre!("CANNOT_GET_BLOCK"));
64            }
65        }
66
67        let ret = AnvilDebugProvider {
68            _node: node_provider,
69            _anvil: anvil_provider,
70            _anvil_instance: Some(Arc::new(anvil)),
71            block_number: BlockNumberOrTag::Number(block),
72            _n: PhantomData::<Ethereum>,
73        };
74
75        let curblock = ret._anvil.get_block_by_number(BlockNumberOrTag::Latest).await?;
76
77        match curblock {
78            Some(curblock) => {
79                if curblock.header.number != block {
80                    return Err(eyre!("INCORRECT_BLOCK_NUMBER"));
81                }
82            }
83            _ => {
84                return Err(eyre!("CANNOT_GET_BLOCK"));
85            }
86        }
87
88        Ok(ret)
89    }
90}
91
92impl<PN, PA, N> AnvilDebugProvider<PN, PA, N>
93where
94    N: Network,
95    PA: Provider<N> + Send + Sync + Clone + 'static,
96    PN: Provider<N> + Send + Sync + Clone + 'static,
97{
98    pub fn new(_node: PN, _anvil: PA, block_number: BlockNumberOrTag) -> Self {
99        Self { _node, _anvil, _anvil_instance: None, block_number, _n: PhantomData }
100    }
101
102    pub fn node(&self) -> &PN {
103        &self._node
104    }
105    pub fn anvil(&self) -> &PA {
106        &self._anvil
107    }
108
109    pub fn privkey(&self) -> Result<SecretKey> {
110        match &self._anvil_instance {
111            Some(anvil) => Ok(anvil.clone().keys()[0].clone()),
112            _ => Err(eyre!("NO_ANVIL_INSTANCE")),
113        }
114    }
115}
116
117/*
118impl<PN, PA, TN, TA, N> Provider<TA, N> for AnvilDebugProvider<PN, PA, TN, TA, N>
119    where
120        TN: Transport + Clone,
121        TA: Transport + Clone,
122        N: Network,
123        PN: Provider<TN, N> + Send + Sync + Clone + 'static,
124        PA: Provider<TA, N> + Send + Sync + Clone + 'static
125{
126    #[inline(always)]
127    fn root(&self) -> &RootProvider<TA, N> {
128        self._anvil.root()
129    }
130
131
132    fn get_block_number(&self) -> RpcCall<TA, (), u64> {
133        self._anvil.get_block_number()
134    }
135}
136
137 */
138
139impl<PN, PA> Provider<Ethereum> for AnvilDebugProvider<PN, PA, Ethereum>
140where
141    PN: Provider<Ethereum> + Send + Sync + Clone + 'static,
142    PA: Provider<Ethereum> + Send + Sync + Clone + 'static,
143{
144    #[inline(always)]
145    fn root(&self) -> &RootProvider<Ethereum> {
146        self._anvil.root()
147    }
148
149    #[allow(clippy::type_complexity)]
150    fn get_block_number(&self) -> ProviderCall<NoParams, U64, BlockNumber> {
151        self._anvil.get_block_number()
152    }
153}
154
155#[async_trait]
156pub trait DebugProviderExt<N: Network = Ethereum> {
157    async fn geth_debug_trace_call(
158        &self,
159        tx: N::TransactionRequest,
160        block: BlockId,
161        trace_options: GethDebugTracingCallOptions,
162    ) -> TransportResult<GethTrace>;
163    async fn geth_debug_trace_block_by_number(
164        &self,
165        block: BlockNumberOrTag,
166        trace_options: GethDebugTracingOptions,
167    ) -> TransportResult<Vec<TraceResult>>;
168    async fn geth_debug_trace_block_by_hash(
169        &self,
170        block: BlockHash,
171        trace_options: GethDebugTracingOptions,
172    ) -> TransportResult<Vec<TraceResult>>;
173}
174
175#[async_trait]
176impl<N> DebugProviderExt<N> for RootProvider<N>
177where
178    N: Network<TransactionRequest = TransactionRequest>,
179{
180    async fn geth_debug_trace_call(
181        &self,
182        tx: N::TransactionRequest,
183        block: BlockId,
184        trace_options: GethDebugTracingCallOptions,
185    ) -> TransportResult<GethTrace> {
186        self.debug_trace_call(tx, block, trace_options).await
187    }
188    async fn geth_debug_trace_block_by_number(
189        &self,
190        block: BlockNumberOrTag,
191        trace_options: GethDebugTracingOptions,
192    ) -> TransportResult<Vec<TraceResult>> {
193        self.debug_trace_block_by_number(block, trace_options).await
194    }
195    async fn geth_debug_trace_block_by_hash(
196        &self,
197        block: BlockHash,
198        trace_options: GethDebugTracingOptions,
199    ) -> TransportResult<Vec<TraceResult>> {
200        self.debug_trace_block_by_hash(block, trace_options).await
201    }
202}
203
204#[async_trait]
205impl<PN, PA, N> DebugProviderExt<N> for AnvilDebugProvider<PN, PA, N>
206where
207    N: Network<TransactionRequest = TransactionRequest>,
208    PN: Provider<N> + Send + Sync + Clone + 'static,
209    PA: Provider<N> + Send + Sync + Clone + 'static,
210{
211    async fn geth_debug_trace_call(
212        &self,
213        tx: N::TransactionRequest,
214        block: BlockId,
215        trace_options: GethDebugTracingCallOptions,
216    ) -> TransportResult<GethTrace> {
217        let block = match block {
218            BlockId::Hash(hash) => BlockId::Hash(hash),
219            BlockId::Number(number) => match number {
220                BlockNumberOrTag::Number(number) => BlockId::Number(BlockNumberOrTag::Number(number)),
221                BlockNumberOrTag::Latest => BlockId::Number(self.block_number),
222                _ => block,
223            },
224        };
225        self._node.debug_trace_call(tx, block, trace_options).await
226    }
227    async fn geth_debug_trace_block_by_number(
228        &self,
229        block: BlockNumberOrTag,
230        trace_options: GethDebugTracingOptions,
231    ) -> TransportResult<Vec<TraceResult>> {
232        self._node.debug_trace_block_by_number(block, trace_options).await
233    }
234    async fn geth_debug_trace_block_by_hash(
235        &self,
236        block: BlockHash,
237        trace_options: GethDebugTracingOptions,
238    ) -> TransportResult<Vec<TraceResult>> {
239        self._node.debug_trace_block_by_hash(block, trace_options).await
240    }
241}
242
243#[cfg(test)]
244mod test {
245    use super::*;
246    use alloy::primitives::{Address, U256};
247    use alloy_provider::ProviderBuilder;
248    use alloy_rpc_client::ClientBuilder;
249    use env_logger::Env as EnvLog;
250    use eyre::Result;
251    use tracing::{debug, error};
252
253    #[tokio::test]
254    async fn test_debug_trace_call() -> Result<()> {
255        let _ = env_logger::try_init_from_env(
256            EnvLog::default().default_filter_or("info,hyper_util=off,alloy_transport_http=off,alloy_rpc_client=off,reqwest=off"),
257        );
258
259        dotenvy::from_filename(".env.test").ok();
260        let node_url = url::Url::parse(std::env::var("MAINNET_HTTP")?.as_str())?;
261
262        let provider_anvil =
263            ProviderBuilder::new().connect_anvil_with_config(|x| x.chain_id(1).fork(node_url.clone()).fork_block_number(20322777));
264
265        let client_node = ClientBuilder::default().http(node_url);
266
267        let provider_node = ProviderBuilder::new().disable_recommended_fillers().connect_client(client_node);
268
269        let provider = AnvilDebugProvider::new(provider_node, provider_anvil, BlockNumberOrTag::Number(10));
270
271        let client = provider;
272
273        let block_number = client.get_block_number().await?;
274
275        let contract: Address = "0x90e7a93e0a6514cb0c84fc7acc1cb5c0793352d2".parse()?;
276        let location: U256 = U256::from(0);
277
278        let cell0 = client.get_storage_at(contract, location).block_id(BlockNumberOrTag::Latest.into()).await?;
279        debug!("{} {}", block_number, cell0);
280
281        match client
282            .geth_debug_trace_call(
283                TransactionRequest::default(),
284                BlockId::Number(BlockNumberOrTag::Latest),
285                GethDebugTracingCallOptions::default(),
286            )
287            .await
288        {
289            Ok(trace) => {
290                debug!("Ok {:?}", trace);
291            }
292            Err(e) => {
293                error!("Error :{}", e);
294                panic!("DEBUG_TRACE_CALL_FAILED");
295            }
296        }
297
298        Ok(())
299    }
300}