1use crate::Cast;
2use alloy_provider::Provider;
3use clap::Parser;
4use eyre::Result;
5use foundry_cli::{
6 json::print_scalar,
7 opts::RpcOpts,
8 utils::{self, LoadConfig},
9};
10use futures::join;
11
12#[derive(Clone, Debug, Parser)]
14pub struct FindBlockArgs {
15 timestamp: u64,
17
18 #[command(flatten)]
19 rpc: RpcOpts,
20}
21
22impl FindBlockArgs {
23 pub async fn run(self) -> Result<()> {
24 let Self { timestamp, rpc } = self;
25
26 let ts_target = timestamp;
27 let config = rpc.load_config()?;
28 let provider = utils::get_provider(&config)?;
29
30 let last_block_num = provider.get_block_number().await?;
31 let cast_provider = Cast::new(provider);
32
33 let res = join!(cast_provider.timestamp(last_block_num), cast_provider.timestamp(1));
34 let ts_block_latest: u64 = res.0?.to();
35 let ts_block_1: u64 = res.1?.to();
36
37 let block_num = if ts_block_latest < ts_target {
38 last_block_num
40 } else if ts_block_1 > ts_target {
41 1
43 } else {
44 let mut low_block = 1_u64; let mut high_block = last_block_num;
47 let mut matching_block = None;
48 while high_block > low_block && matching_block.is_none() {
49 let high_minus_low_over_2 = high_block
51 .checked_sub(low_block)
52 .ok_or_else(|| eyre::eyre!("unexpected underflow"))
53 .unwrap()
54 .checked_div(2_u64)
55 .unwrap();
56 let mid_block = high_block.checked_sub(high_minus_low_over_2).unwrap();
57 let ts_mid_block = cast_provider.timestamp(mid_block).await?.to::<u64>();
58
59 if ts_mid_block == ts_target {
61 matching_block = Some(mid_block)
62 } else if high_block.checked_sub(low_block).unwrap() == 1_u64 {
63 let res = join!(
66 cast_provider.timestamp(high_block),
67 cast_provider.timestamp(low_block)
68 );
69 let ts_high: u64 = res.0.unwrap().to();
70 let ts_low: u64 = res.1.unwrap().to();
71 let high_diff = ts_high.checked_sub(ts_target).unwrap();
72 let low_diff = ts_target.checked_sub(ts_low).unwrap();
73 let is_low = low_diff < high_diff;
74 matching_block = if is_low { Some(low_block) } else { Some(high_block) }
75 } else if ts_mid_block < ts_target {
76 low_block = mid_block;
77 } else {
78 high_block = mid_block;
79 }
80 }
81 matching_block.unwrap_or(low_block)
82 };
83 print_scalar(block_num)?;
84
85 Ok(())
86 }
87}