kabu_node_debug_provider/
debugprovider.rs1use 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#[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_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
117impl<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}