contract_verification_migrator/
lib.rs

1//! Copy contract verification from one block-explorer to another
2//!
3//! This crate allows you to easily copy the verfied source code from one block-explorer to another.
4//! It reads the source code / metadata from the source_url and submits it to the target_url.
5//! This assumes that both block-explorers are compatible with the the etherscan api specification
6//!
7//! ```rust
8//!    let results = contract_verification_migrator::copy_etherscan_verification(
9//!        vec!["0xE592427A0AEce92De3Edee1F18E0157C05861564".to_string()],
10//!        "<YOUR_ETHERSCAN_API_KEY>".to_string(),
11//!        "https://api.etherscan.io/api".to_string(),
12//!        "<YOUR_BLOCKSCOUT_API_KEY>".to_string(),
13//!        "https://eth.blockscout.com/api".to_string(),
14//!        true,
15//!    );
16//! ```
17#![warn(missing_docs)]
18
19use eyre::Result;
20use futures::future::FutureExt;
21
22mod verification;
23pub use verification::{copy_etherscan_verification_for_contract, VerificationResult};
24mod progress_bar;
25use progress_bar::{initialize_multi_progress, initialize_progress_bar, update_progress_bar};
26
27/// Copy contract verification of multiple contracts from one block-explorer to another
28///
29/// # Arguments
30/// - `contract_addresses` - Vector of contract addresses for which to copy the contract
31/// verification
32/// - `source_api_key` - The api key for the source block-explorer's api
33/// - `source_url` - The url of the source block-explorer's api
34/// - `target_api_key` - The api key for the target block-explorer's api
35/// - `target_url` - The url of the target block-explorer's api
36/// - `progress_bar` - Boolean indicating wether or not to display progress bars for the individual
37/// requests
38///
39/// # Examples
40///
41/// ```rust
42///    let results = contract_verification_migrator::copy_etherscan_verification(
43///        vec!["0xE592427A0AEce92De3Edee1F18E0157C05861564".to_string()],
44///        "<YOUR_ETHERSCAN_API_KEY>".to_string(),
45///        "https://api.etherscan.io/api".to_string(),
46///        "<YOUR_BLOCKSCOUT_API_KEY>".to_string(),
47///        "https://eth.blockscout.com/api".to_string(),
48///        true,
49///     );
50///
51/// ```
52pub async fn copy_etherscan_verification(
53    contract_addresses: Vec<String>,
54    source_api_key: String,
55    source_url: String,
56    target_api_key: String,
57    target_url: String,
58    progress_bar: bool,
59) -> Vec<Result<VerificationResult>> {
60    let mp = initialize_multi_progress(progress_bar);
61    let tasks: Vec<_> = contract_addresses
62        .into_iter()
63        .map(move |contract_address| {
64            let pb = initialize_progress_bar(mp.clone(), &contract_address);
65            copy_etherscan_verification_for_contract(
66                contract_address.clone(),
67                source_api_key.clone(),
68                source_url.clone(),
69                target_api_key.clone(),
70                target_url.clone(),
71            )
72            .then(move |result| {
73                update_progress_bar(pb, &result);
74                futures::future::ready(result)
75            })
76        })
77        .collect();
78    futures::future::join_all(tasks).await
79}
80
81#[cfg(test)]
82mod tests {
83    use super::*;
84
85    #[tokio::test]
86    async fn test_copy_verification_from_etherscan_to_blockscout() {
87        let results = copy_etherscan_verification(
88            contract_addresses(),
89            etherscan_api_key(),
90            etherscan_url(),
91            blockscout_api_key(),
92            blockscout_url(),
93            false,
94        )
95        .await;
96        assert!(!results.into_iter().any(|result| result.is_err()));
97    }
98
99    #[tokio::test]
100    // TODO: Re-enable test once the fix in foundry-block-explorers is pushed to crates.io
101    // See: https://github.com/foundry-rs/block-explorers/commit/472118dafcbeb9bfdcd8df1c11ef28a5c51884d9
102    #[ignore]
103    async fn test_copy_verification_from_blockscout_to_etherscan() {
104        let results = copy_etherscan_verification(
105            contract_addresses(),
106            blockscout_api_key(),
107            blockscout_url(),
108            etherscan_api_key(),
109            etherscan_url(),
110            false,
111        )
112        .await;
113        assert!(!results.into_iter().any(|result| result.is_err()));
114    }
115
116    // Complex contract verified in "standard-solidity-json" format (non-flattened)
117    const UNI_V3_ROUTER: &str = "0xE592427A0AEce92De3Edee1F18E0157C05861564";
118    // Complex contract verified in flattened format on etherscan
119    const ICETH_TOKEN: &str = "0x7C07F7aBe10CE8e33DC6C5aD68FE033085256A84";
120    // Proxy contract - note that this should only attempt to migrate / copy the proxy itself
121    // and not the implementation contract
122    const BLUR: &str = "0x000000000000Ad05Ccc4F10045630fb830B95127";
123
124    fn contract_addresses() -> Vec<String> {
125        vec![
126            UNI_V3_ROUTER.to_string(),
127            ICETH_TOKEN.to_string(),
128            BLUR.to_string(),
129        ]
130    }
131    fn etherscan_url() -> String {
132        "https://api.etherscan.io/api".to_string()
133    }
134    fn etherscan_api_key() -> String {
135        std::env::var("ETHERSCAN_API_KEY").expect("ETHERSCAN_API_KEY not set")
136    }
137
138    fn blockscout_url() -> String {
139        "https://eth.blockscout.com/api".to_string()
140    }
141
142    fn blockscout_api_key() -> String {
143        std::env::var("BLOCKSCOUT_API_KEY").expect("BLOCKSCOUT_API_KEY not set")
144    }
145}